diff --git a/.bumpversion.cfg b/.bumpversion.cfg index f21ee42a7..486c4c846 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,4 +1,4 @@ [bumpversion] -current_version = 1.6.9 +current_version = 1.8.9 files = version.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..f3a5cd4ea --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[run] +omit = + .tox/* diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..2e0a155c0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,18 @@ + +* text=false + +*.bas -crlf +*.asm -crlf +*.bi -crlf + +*.txt text +*.md text +*.py text +*.ini text +*.yml text + +*.png binary + +*.bas linguist-vendored +*.asm linguist-vendored + diff --git a/.gitignore b/.gitignore index 280cb5e75..faa4ff652 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.egg-info/ *.pyc .cache/ +.pytest_cache/ .idea/ .python-version .tox/ @@ -16,3 +17,5 @@ dist/ examples/*.bin examples/*.tzx scratch/ +.coverage +htmlcov/ diff --git a/.travis.yml b/.travis.yml index 7b8fbfca9..e324d728d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: python python: # - "2.7" <- Unsupported, uses 2.7.9 ? - - "3.5" + - "3.6" # PyPy versions # - "pypy" # PyPy2 2.5.0 # - "pypy3" # Pypy3 2.4.0 diff --git a/ChangeLog b/ChangeLog index c81122f9a..f2ed395ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,107 @@ +================================================================ +Changes from Version 1.8.8 to 1.8.9 +! Bugfix: Crash in READ and DATA sentences under some cases +! Bugfix: Fix INT to behave like the original one (Round to -INF) +! Bugfix: --array-check was not working properly. Fixed! + +================================================================ +Changes from Version 1.8.7 to 1.8.8 +! Bugfix: fix 32 bit operations (DIV, MOD...) + +================================================================ +Changes from Version 1.8.6 to 1.8.7 +! Bugfix: do not remove ASM blocks (optimize) + +================================================================ +Changes from Version 1.8.5 to 1.8.6 +! Bugfix: END instruction was not returning result. Fixed. + +================================================================ +Changes from Version 1.8.4 to 1.8.5 +! Bugfix: crash on bad array declaration + +================================================================ +Changes from Version 1.8.3 to 1.8.4 +! Several bugfixes with contants declaration ++ Suport for UTF-8 BOM files +! Bugfixes with -O3 crash +! Fixes crash with arrays +! Other bugfixes and better stability +* Better warning explanation under some circumstances + +================================================================ +Changes from Version 1.8.2 to 1.8.3 +! Bugfix in the peephole optimizer (-O2) +! Several bugfixes to improve stability ++ Optimization in the peephole optimizer (-O1) ++ Support for extended array str element operations +! Other syntax bugfixes + +================================================================ +Changes from Version 1.8.1 to 1.8.2 +! Bugfixes in the peephole optimizer ++ Shorter and faster generated code (deep optimizations) +! Bugfix in the PRINT42 routine that now supports newlines, etc ++ Implemented routine input42 (INPUT42.BAS) for PRINT42 mode + +================================================================ +Changes from Version 1.8.0 to 1.8.1 +! Bugfixes in the peephole optimizer +! Bugfix in OUT instruction ++ Fixes minor errors and bugs (i.e. --enable-break) ++ Improved and faster generated code (IN, OUT, AND, check BREAK...) +* Added basic.bas library (meta-interpreter) and eval.bas example!! + (thanks to @mcleod_ideafix!!!) + +================================================================ +Changes from Version 1.7.2 to 1.8.0 +! Bugfixes in the peephole optimizer (-O3) ++ Better optimized code ++ Improved compiling speed and more stability ++ Fixes minor errors and bugs ++ Now single line IF sentences does not require END IF + +================================================================ +Changes from Version 1.7.1 to 1.7.2 +! Bugfixes in libraries esxdos.bas and memcopy.bas +* Improved pong.bas example +* Improved readme file :) (thanks to @harko and @haplo) + +================================================================ +Changes from Version 1.7.0 to 1.7.1 +! Bugfixes with -O3 and DATA statements +* Little improvements +* Updates README.md and added TESTING.md + +================================================================ +Changes from Version 1.6.13 to 1.7.0 ++ Added READ, DATA, RESTORE (finally!) ++ Allows to call SUBs with no parenthesis (e.g. mySUB 1, 2+a) ++ Allows to call FUNCTIONS with 1 or no params with no parenthesis ++ Some bug fixes for better stability + +================================================================ +Changes from Version 1.6.12 to 1.6.13 +! Fixes and improves strict mode checking ++ Adds #error and #warning directives + +================================================================ +Changes from Version 1.6.11 to 1.6.12 ++ Adds missing default font (Haplo) for Radastan mode +! Bugfixes and little improvements + +================================================================ +Changes from Version 1.6.10 to 1.6.11 +! Fix infinite recursive include in Windows (yes, win sucks) +* Little optimizations in memset and rnd +* Standarize file includes like in cpp + ================================================================ Changes from Version 1.6.9 to 1.6.10 -* Added UART library (by yomboprime) for serial communication -* Several bugfixes and minor errors ++ Added many more drawing primitives for Radastan Mode ++ Added instructions ON .. GOTO and ON .. GOSUB ++ Added UART library (by yomboprime) for serial communication +! Several bugfixes and minor errors and better stability * Better code generation * Allows array initialization with @label references * Switch .bas libraries (not the compiler) to MIT license @@ -56,6 +156,19 @@ and attribute traversal which depends on the symbolTYPE being parsed. e.g. for symbolBINARY (binary expressions), we have node.left, node.right, node.operand, but also node.children[0], node.children[1]. +===From Version 1.2.9 to 1.3.0=== +* ! Fixed a bug in USR +* ! Fixed a bug in SAVE / LOAD +* ! Fixed a serious bug in the preprocessor +* ! Fixed a bug with DIM and constants +* ! Fixed a bug with SHL/SHR for 0 shifts +* + Added -D option. ZXBASIC now allows commandline macro definition +* ! Fixed a bug with CODE and INKEY$ +* ! Fixed a bug with string slicing assignation (e.g. a$(3) = "x") +* ! Fixed a bug with arrays of integer assignation (e.g. a(3) = 5, being a of Integer type) +* ! Fixed a bug with peephole optimizer (-O3) +* * Some changes and code refactorization towards 2.x branch + ================================================================ Changes from Version 1.2.8 to 1.2.9 diff --git a/README.md b/README.md index e91f0e742..18e1c1353 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ +![Boriel ZX Basic](./zxbasic_logo.png) + [![Build Status](https://travis-ci.org/boriel/zxbasic.svg?branch=master)](https://travis-ci.org/boriel/zxbasic) +[![license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE.txt) +[![pyversions](https://img.shields.io/pypi/pyversions/zxbasic.svg)](https://pypi.python.org/pypi/zxbasic) ZX BASIC -------- @@ -30,43 +34,70 @@ For help, support, updates meet the community at my forum: -INSTALL -------- +INSTALLATION +------------ Go to the ZXBasic download page -and get the version most suitable for you. +and get the version most suitable for you. -These tools are completely written in python, so you will need a python -interpreter (available on many platforms). Just copy them in a directory of your -hoice and installation is done. :-) +There are, basically, two flavors (both with identical capabilities): -For Windows users there is also a binary .MSI installation, which does not need -python installed. + - For Windows you can download de win32 executable (Windows .exe zip package) version. +To install just uncompress it in a directory of your choice. +The main executable is `zxb.exe` (more on this later). With this toolchain +also comes `zxbasm.exe` (the assembler) and `zxbpp.exe` (the preprocessor), but these +are not needed when programming in BASIC. + - For Linux and Mac OSX there is a python version, so you will need a python +interpreter (available on many platforms, and usually already installed in Linux and Mac OSX). +Just uncompress it in a directory of your choice and installation is done. :-) +The main executables are `zxb.py` (the compiler), `zxbasm.py` (the assembler) and `zxbpp.py` (the preprocessor). +You can use this version in Windows, but will need to install a python interpreter first. -TESTING -------- +##### Examples -You will need to get Tox in order to run the project tests. Normally it is done -by calling: -~~~~ -$ pip install tox -~~~~ +|![Eleuterio, el mono serio](http://www.boriel.com/wiki/en/images/a/ab/EleuterioElMonoSerio.gif)|![El Hobbit](http://www.boriel.com/wiki/en/images/7/72/HobbitEl.gif)|![Knight & Demonds DX](http://www.boriel.com/wiki/en/images/f/fe/KnightsDemonsDX.png)| +|---|---|---| +| An in-game screenshot of Eleuterio by @*na_th_an* | Ingame screenshot of _El Hobbit_ by @*Wilco2000*| Ingame screenshot of _Knignt and Demonds DX_ by Einar Saukas + +See more examples at http://www.boriel.com/wiki/en/index.php/ZX_BASIC:Released_Programs -inside a Virtual Environment ( https://virtualenv.pypa.io/en/stable/ ). +QUICK START +----------- -Please, see https://tox.readthedocs.io/en/latest/install.html for more -information about installing Tox. +For a quick start, just open a terminal in your PC in the same directory you uncompressed ZX Basic +and type `zxb` (on Windows) or `zxb.py` (OSX, Linux). You should see a zxbasic message like this: -Once you have installed Tox, just call: +``` +usage: zxb [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE] [-T] [-t] [-B] [-a] [-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] + [--version] + PROGRAM +zxb: error: the following arguments are required: PROGRAM +``` + +Create a text file with the following content: + +~~~~ +10 CLS +20 PRINT "HELLO WORLD!" +~~~~ +Save it as `hello.bas` and finally compile it with: ~~~~ -$ tox +zxb -taB hello.bas ~~~~ -to get your tests running. +If everything went well, a file named `hello.tap` should be created. +Open it with your favourite emulator (i.e. fuse) and see the result. +Congratulations! You're now ready to create compiled BASIC programs for +your machine. Check and compile the examples included in the examples/ folder +or go to the Wiki for further info. AKNOWLEDGEMENTS --------------- diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 000000000..6f4251daf --- /dev/null +++ b/TESTING.md @@ -0,0 +1,32 @@ +TESTING +------- + +This instructions are for TDD (Test Drive Development) the compiler and are intended +for developing the compiler internals, and checks for bugs in it. + +You **DONT** need to do this to compile BASIC. + +You are required to be familiar not only with python, but also with the python +ecosystem (virtualenvs, tox, py.test). + +You will need to get Tox in order to run the project tests. Normally it is done +by calling: + +~~~~ +$ pip install tox +~~~~ + +Inside a Virtual Environment of your choice ( https://virtualenv.pypa.io/en/stable/ ). + +Please, see https://tox.readthedocs.io/en/latest/install.html for more +information about installing Tox. + +Once you have installed Tox, just call: + +~~~~ +$ tox +~~~~ + +to get your tests running. + +This tox config also supports detox (parallel tox testing). \ No newline at end of file diff --git a/api/check.py b/api/check.py index 41ed18329..6817bb29b 100644 --- a/api/check.py +++ b/api/check.py @@ -13,10 +13,13 @@ from . import global_ from .constants import CLASS from .constants import SCOPE +import api.errmsg from .errmsg import syntax_error + __all__ = ['check_type', - 'check_is_declared_strict', + 'check_is_declared_explicit', + 'check_type_is_explicit', 'check_call_arguments', 'check_pending_calls', 'check_pending_labels', @@ -58,10 +61,10 @@ def check_type(lineno, type_list, arg): return False -def check_is_declared_strict(lineno, id_, classname='variable'): +def check_is_declared_explicit(lineno, id_, classname='variable'): """ Check if the current ID is already declared. If not, triggers a "undeclared identifier" error, - if the --strict command line flag is enabled (or #pragma + if the --explicit command line flag is enabled (or #pragma option strict is in use). If not in strict mode, passes it silently. @@ -73,6 +76,14 @@ def check_is_declared_strict(lineno, id_, classname='variable'): return entry is not None # True if declared +def check_type_is_explicit(lineno, id_, type_): + from symbols.type_ import SymbolTYPE + assert isinstance(type_, SymbolTYPE) + if type_.implicit: + if config.OPTIONS.strict.value: + api.errmsg.syntax_error_undeclared_type(lineno, id_) + + def check_call_arguments(lineno, id_, args): """ Check arguments against function signature. @@ -167,12 +178,31 @@ def check_pending_labels(ast): return result +def check_and_make_label(lbl, lineno): + """ Checks if the given label (or line number) is valid and, if so, + returns a label object. + :param lbl: Line number of label (string) + :param lineno: Line number in the basic source code for error reporting + :return: Label object or None if error. + """ + if isinstance(lbl, float): + if lbl == int(lbl): + id_ = str(int(lbl)) + else: + syntax_error(lineno, 'Line numbers must be integers.') + return None + else: + id_ = lbl + + return global_.SYMBOL_TABLE.access_label(id_, lineno) + + # ---------------------------------------------------------------------- # Function for checking some arguments # ---------------------------------------------------------------------- def is_null(*symbols): """ True if no nodes or all the given nodes are either - None, NOP or empty blocks. + None, NOP or empty blocks. For blocks this applies recursively """ from symbols.symbol_ import Symbol @@ -183,7 +213,9 @@ def is_null(*symbols): return False if sym.token == 'NOP': continue - if sym.token == 'BLOCK' and not sym: + if sym.token == 'BLOCK': + if not is_null(*sym.children): + return False continue return False return True @@ -202,6 +234,10 @@ def is_SYMBOL(token, *symbols): return True +def is_LABEL(*p): + return is_SYMBOL('LABEL', *p) + + def is_string(*p): return is_SYMBOL('STRING', *p) @@ -221,9 +257,9 @@ def is_CONST(*p): def is_static(*p): """ A static value (does not change at runtime) - which is known at compile time + which is known at compile time """ - return all(is_SYMBOL('CONST', x) or + return all(is_CONST(x) or is_number(x) or is_const(x) for x in p) @@ -302,6 +338,8 @@ def is_signed(*p): def is_numeric(*p): + """ Returns false unless all elements in p are of numerical type + """ from symbols.type_ import Type try: @@ -350,6 +388,29 @@ def is_dynamic(*p): # TODO: Explain this better return False +def is_callable(*p): + """ True if all the args are functions and / or subroutines + """ + import symbols + return all(isinstance(x, symbols.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 + """ + if is_LABEL(block) and block.accessed: + return True + + for child in block.children: + if not is_callable(child) and is_block_accessed(child): + return True + + return False + + def common_type(a, b): """ Returns a type which is common for both a and b types. Returns None if no common types allowed. diff --git a/api/config.py b/api/config.py index 0923ceced..9868e8d29 100644 --- a/api/config.py +++ b/api/config.py @@ -14,6 +14,7 @@ # The options container from . import options from . import global_ +from .options import ANYTYPE # ------------------------------------------------------ # Common setup and configuration for all tools @@ -30,9 +31,9 @@ def init(): OPTIONS.add_option('Debug', int, 0) # Default console redirections - OPTIONS.add_option('stdin', None, sys.stdin) - OPTIONS.add_option('stdout', None, sys.stdout) - OPTIONS.add_option('stderr', None, sys.stderr) + OPTIONS.add_option('stdin', ANYTYPE, sys.stdin) + OPTIONS.add_option('stdout', ANYTYPE, sys.stdout) + OPTIONS.add_option('stderr', ANYTYPE, sys.stderr) # ---------------------------------------------------------------------- # Default Options and Compilation Flags @@ -54,7 +55,7 @@ def init(): OPTIONS.add_option('use_loader', bool, False) # Whether to use a loader OPTIONS.add_option('autorun', bool, False) # Whether to add autostart code (needs basic loader = true) - OPTIONS.add_option('output_file_type', str, '"bin"') # bin, tap, tzx etc... + OPTIONS.add_option('output_file_type', str, 'bin') # bin, tap, tzx etc... OPTIONS.add_option('include_path', str, '') # Include path, like '/var/lib:/var/include' OPTIONS.add_option('memoryCheck', bool, False) @@ -66,6 +67,7 @@ def init(): OPTIONS.add_option('__DEFINES', dict, {}) OPTIONS.add_option('explicit', bool, False) OPTIONS.add_option('Sinclair', bool, False) + OPTIONS.add_option('strict', bool, False) # True to force type checking init() diff --git a/api/errmsg.py b/api/errmsg.py index 6798b4df7..0562a343d 100644 --- a/api/errmsg.py +++ b/api/errmsg.py @@ -17,6 +17,14 @@ __all__ = ['syntax_error', 'warning'] +def msg_output(msg): + if msg in global_.error_msg_cache: + return + + OPTIONS.stderr.value.write("%s\n" % msg) + global_.error_msg_cache.add(msg) + + def syntax_error(lineno, msg): """ Generic syntax error routine """ @@ -24,8 +32,7 @@ def syntax_error(lineno, msg): msg = 'Too many errors. Giving up!' msg = "%s:%i: %s" % (global_.FILENAME, lineno, msg) - - OPTIONS.stderr.value.write("%s\n" % msg) + msg_output(msg) if global_.has_errors > OPTIONS.max_syntax_errors.value: sys.exit(1) @@ -37,14 +44,17 @@ def warning(lineno, msg): """ Generic warning error routine """ msg = "%s:%i: warning: %s" % (global_.FILENAME, lineno, msg) - OPTIONS.stderr.value.write("%s\n" % msg) - + msg_output(msg) global_.has_warnings += 1 def warning_implicit_type(lineno, id_, type_=None): """ Warning: Using default implicit type 'x' """ + if OPTIONS.strict.value: + syntax_error_undeclared_type(lineno, id_) + return + if type_ is None: type_ = global_.DEFAULT_TYPE @@ -69,6 +79,12 @@ def warning_empty_loop(lineno): warning(lineno, 'Empty loop') +def warning_empty_if(lineno): + """ Warning: Useless empty IF ignored + """ + warning(lineno, 'Useless empty IF ignored') + + # Emmits an optimization warning def warning_not_used(lineno, id_, kind='Variable'): if OPTIONS.optimization.value > 0: @@ -145,3 +161,17 @@ def syntax_error_cant_convert_to_type(lineno, expr_str, type_): # ---------------------------------------- def syntax_error_is_a_sub_not_a_func(lineno, name): syntax_error(lineno, "'%s' is SUBROUTINE not a FUNCTION" % name) + + +# ---------------------------------------- +# Syntax error: strict mode: missing type declaration +# ---------------------------------------- +def syntax_error_undeclared_type(lineno, id_): + syntax_error(lineno, "strict mode: missing type declaration for '%s'" % id_) + + +# ---------------------------------------- +# Cannot assign a value to 'var'. It's not a variable +# ---------------------------------------- +def syntax_error_cannot_assing_not_a_var(lineno, id_): + syntax_error(lineno, "Cannot assign a value to '%s'. It's not a variable" % id_) diff --git a/api/global_.py b/api/global_.py index e9cbbc7b2..488dee901 100644 --- a/api/global_.py +++ b/api/global_.py @@ -136,3 +136,17 @@ # Default optimization level # ---------------------------------------------------------------------- DEFAULT_OPTIMIZATION_LEVEL = 2 # Optimization level. Higher -> more optimized + +# ---------------------------------------------------------------------- +# DATA blocks +# ---------------------------------------------------------------------- +DATAS = [] +DATA_LABELS = {} # Maps declared labels to current data ptr +DATA_PTR_CURRENT = None +DATA_IS_USED = False +DATA_FUNCTIONS = [] # Counts the number of funcptrs emitted + +# ---------------------------------------------------------------------- +# Cache of Message errors to avoid repetition +# ---------------------------------------------------------------------- +error_msg_cache = set() diff --git a/api/optimize.py b/api/optimize.py index 416cdbf22..d48fd684e 100644 --- a/api/optimize.py +++ b/api/optimize.py @@ -3,6 +3,7 @@ from ast_ import NodeVisitor from .config import OPTIONS +import api.errmsg from api.errmsg import warning import api.check as chk from api.constants import TYPE @@ -21,11 +22,10 @@ def __init__(self, obj): self.obj = obj -# TODO: Implement as a visitor with generators class OptimizerVisitor(NodeVisitor): """ Implements some optimizations """ - NOP = symbols.SENTENCE('NOP') # Return this for "erased" nodes + NOP = symbols.NOP() # Return this for "erased" nodes @staticmethod def TYPE(type_): @@ -133,6 +133,14 @@ def visit_FUNCDECL(self, node): warning(node.entry.lineno, "Function '%s' is never called and has been ignored" % node.entry.name) yield self.NOP else: + node.children[1] = (yield ToVisit(node.entry)) + yield node + + def visit_FUNCTION(self, node): + if getattr(node, 'visited', False): + yield node + else: + node.visited = True yield (yield self.generic_visit(node)) def visit_LET(self, node): @@ -142,6 +150,13 @@ def visit_LET(self, node): else: yield (yield self.generic_visit(node)) + def visit_LETSUBSTR(self, node): + if self.O_LEVEL > 1 and not node.children[0].accessed: + warning_not_used(node.children[0].lineno, node.children[0].name) + yield self.NOP + else: + yield (yield self.generic_visit(node)) + def visit_RETURN(self, node): """ Visits only children[1], since children[0] points to the current function being returned from (if any), and @@ -157,9 +172,80 @@ def visit_UNARY(self, node): else: yield (yield self.generic_visit(node)) + def visit_BLOCK(self, node): + if self.O_LEVEL >= 1 and chk.is_null(node): + yield self.NOP + return + yield (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 + + if self.O_LEVEL >= 1: + if chk.is_null(then_, else_): + api.errmsg.warning_empty_if(node.lineno) + yield self.NOP + return + + block_accessed = chk.is_block_accessed(then_) or chk.is_block_accessed(else_) + if not block_accessed and chk.is_number(expr_): # constant condition + if expr_.value: # always true (then_) + yield then_ + else: # always false (else_) + yield else_ + return + + if chk.is_null(else_) and len(node.children) == 3: + node.children.pop() # remove empty else + yield node + return + + for i in range(len(node.children)): + node.children[i] = (expr_, then_, else_)[i] + yield node + + def visit_WHILE(self, node): + expr_ = (yield node.children[0]) + body_ = (yield node.children[1]) + + if self.O_LEVEL >= 1: + if chk.is_number(expr_) and not expr_.value and not chk.is_block_accessed(body_): + yield self.NOP + return + + for i, child in enumerate((expr_, body_)): + node.children[i] = child + yield node + + def visit_FOR(self, node): + from_ = (yield node.children[1]) + to_ = (yield node.children[2]) + step_ = (yield node.children[3]) + body_ = (yield node.children[4]) + + if self.O_LEVEL > 0 and chk.is_number(from_, to_, step_) and not chk.is_block_accessed(body_): + if from_ > to_ and step_ > 0: + yield self.NOP + return + if from_ < to_ and step_ < 0: + yield self.NOP + return + + for i, child in enumerate((from_, to_, step_, body_), start=1): + node.children[i] = child + yield node + + # TODO: ignore unused labels + def _visit_LABEL(self, node): + if self.O_LEVEL and not node.accessed: + yield self.NOP + else: + yield node + @staticmethod def generic_visit(node): for i in range(len(node.children)): node.children[i] = (yield ToVisit(node.children[i])) - yield node diff --git a/api/options.py b/api/options.py index b64d57a22..541852699 100644 --- a/api/options.py +++ b/api/options.py @@ -15,7 +15,14 @@ FALSE = false = False -__all__ = ['Option', 'Options'] +__all__ = ['Option', 'Options', 'ANYTYPE'] + + +class ANYTYPE(object): + """ Dummy class to signal any value + """ + pass + # ---------------------------------------------------------------------- # Exception for duplicated Options @@ -75,10 +82,12 @@ def value(self): @value.setter def value(self, value): - if self.type is not None: + if self.type is not None and not isinstance(value, self.type): try: value = eval(value) - except: + except TypeError: + pass + except ValueError: pass if value is not None and not isinstance(value, self.type): @@ -116,7 +125,7 @@ def reset(self): if self.options is None: self.options = {} - for opt in list(self.options.keys()): + for opt in list(self.options.keys()): # converts to list since dict will change size during iteration self.remove_option(opt) def add_option(self, name, type_=None, default_value=None): @@ -125,6 +134,8 @@ def add_option(self, name, type_=None, default_value=None): if type_ is None and default_value is not None: type_ = type(default_value) + elif type_ is ANYTYPE: + type_ = None self.options[name] = Option(name, type_, default_value) setattr(self, name, self.options[name]) diff --git a/api/symboltable.py b/api/symboltable.py index f6e8b8275..cfbf78077 100644 --- a/api/symboltable.py +++ b/api/symboltable.py @@ -32,7 +32,7 @@ from .constants import TYPE from .check import is_number -from .check import check_is_declared_strict +from .check import check_is_declared_explicit # ---------------------------------------------------------------------- @@ -176,6 +176,8 @@ def declare(self, id_, lineno, entry): if id2[-1] in DEPRECATED_SUFFIXES: id2 = id2[:-1] # Remove it type_ = symbols.TYPEREF(self.basic_types[SUFFIX_TYPE[id_[-1]]], lineno) # Overrides type_ + if entry.type_ is not None and not entry.type_.implicit and type_ != entry.type_: + syntax_error(lineno, "expected type {2} for '{0}', got {1}".format(id_, entry.type_.name, type_.name)) # Checks if already declared if self[self.current_scope][id2] is not None: @@ -190,10 +192,11 @@ def declare(self, id_, lineno, entry): # HINT: The following should be done by the respective callers! # entry.callable = None # True if function, strings or arrays + # entry.class_ = None # TODO: important + entry.forwarded = False # True for a function header entry.mangled = '%s%s%s' % (self.mangle, global_.MANGLE_CHR, entry.name) # Mangled name - # entry.class_ = None # TODO: important - entry.type_ = type_ # HINT: Nonsense. Must be set at declaration or later + entry.type_ = type_ # type_ now reflects entry sigil (i.e. a$ => 'string' type) if any entry.scopeRef = self[self.current_scope] return entry @@ -394,7 +397,7 @@ def access_id(self, id_, lineno, scope=None, default_type=None, default_class=CL default_type = symbols.TYPEREF(default_type, lineno, implicit=False) assert default_type is None or isinstance(default_type, symbols.TYPEREF) - if not check_is_declared_strict(lineno, id_): + if not check_is_declared_explicit(lineno, id_): return None result = self.get_entry(id_, scope) @@ -480,7 +483,7 @@ def access_func(self, id_, lineno, scope=None, default_type=None): return result - def access_call(self, id_, lineno, scope=None): + def access_call(self, id_, lineno, scope=None, type_=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. @@ -492,7 +495,7 @@ def access_call(self, id_, lineno, scope=None): - MyArray(5, 3.7, VAL("32")) makes MyArray identifier "callable". - MyString(5 TO 7) or MyString(5) is a "callable" string. """ - entry = self.access_id(id_, lineno, scope) + entry = self.access_id(id_, lineno, scope, default_type=type_) if entry is None: return self.access_func(id_, lineno) @@ -694,6 +697,8 @@ def declare_param(self, id_, lineno, type_=None): if entry is None: return entry.declared = True + if entry.type_.implicit: + warning_implicit_type(lineno, id_, type_) return entry def declare_array(self, id_, lineno, type_, bounds, default_value=None): diff --git a/api/utils.py b/api/utils.py index e872a96b2..160eec775 100644 --- a/api/utils.py +++ b/api/utils.py @@ -5,6 +5,9 @@ from . import global_ from . import errmsg + +__all__ = ['read_txt_file', 'open_file', 'sanitize_filename', 'flatten_list'] + __doc__ = """Utils module contains many helpers for several task, like reading files or path management""" @@ -12,7 +15,7 @@ def read_txt_file(fname): """Reads a txt file, regardless of its encoding """ - encodings = ['utf-8', 'cp1252'] + encodings = ['utf-8-sig', 'cp1252'] with open(fname, 'rb') as f: content = bytes(f.read()) @@ -43,3 +46,57 @@ def open_file(fname, mode='rb', encoding='utf-8'): kwargs = {'encoding': encoding} return open(fname, mode, **kwargs) + + +def sanitize_filename(fname): + """ Given a file name (string) returns it with back-slashes reversed. + This is to make all BASIC programs compatible in all OSes + """ + return fname.replace('\\', '/') + + +def current_data_label(): + """ Returns a data label to which all labels must point to, until + a new DATA line is declared + """ + return '__DATA__{0}'.format(len(global_.DATAS)) + + +def flatten_list(x): + result = [] + + for l in x: + if not isinstance(l, list): + result.append(l) + else: + result.extend(flatten_list(l)) + + return result + + +def parse_int(str_num): + """ Given an integer number, return its value, + or None if it could not be parsed. + Allowed formats: DECIMAL, HEXA (0xnnn, $nnnn or nnnnh) + :param str_num: (string) the number to be parsed + :return: an integer number or None if it could not be parsedd + """ + str_num = (str_num or "").strip().upper() + if not str_num: + return None + + base = 10 + if str_num.startswith('0X'): + base = 16 + str_num = str_num[2:] + if str_num.endswith('H'): + base = 16 + str_num = str_num[:-1] + if str_num.startswith('$'): + base = 16 + str_num = str_num[1:] + + try: + return int(str_num, base) + except ValueError: + return None diff --git a/arch/zx48k/backend/__16bit.py b/arch/zx48k/backend/__16bit.py index 6297503a4..f13c50daa 100644 --- a/arch/zx48k/backend/__16bit.py +++ b/arch/zx48k/backend/__16bit.py @@ -288,15 +288,14 @@ def _divu16(ins): ''' op1, op2 = tuple(ins.quad[2:]) if is_int(op1) and int(op1) == 0: # 0 / A = 0 + if op2[0] in ('_', '$'): + output = [] # Optimization: Discard previous op if not from the stack + else: + output = _16bit_oper(op2) # Normalize stack - if op2[0] in ('_', '$'): - output = [] # Optimization: Discard previous op if not from the stack - else: - output = _16bit_oper(op2) # Normalize stack - - output.append('ld hl, 0') - output.append('push hl') - return output + output.append('ld hl, 0') + output.append('push hl') + return output if is_int(op2): op = int16(op2) @@ -349,15 +348,14 @@ def _divi16(ins): ''' op1, op2 = tuple(ins.quad[2:]) if is_int(op1) and int(op1) == 0: # 0 / A = 0 + if op2[0] in ('_', '$'): + output = [] # Optimization: Discard previous op if not from the stack + else: + output = _16bit_oper(op2) # Normalize stack - if op2[0] in ('_', '$'): - output = [] # Optimization: Discard previous op if not from the stack - else: - output = _16bit_oper(op2) # Normalize stack - - output.append('ld hl, 0') - output.append('push hl') - return output + output.append('ld hl, 0') + output.append('push hl') + return output if is_int(op2): op = int16(op2) diff --git a/arch/zx48k/backend/__8bit.py b/arch/zx48k/backend/__8bit.py index 7932dc02c..316bcbe46 100644 --- a/arch/zx48k/backend/__8bit.py +++ b/arch/zx48k/backend/__8bit.py @@ -749,9 +749,14 @@ def _and8(ins): return output output = _8bit_oper(op1, op2) - output.append('call __AND8') + # output.append('call __AND8') + lbl = tmp_label() + output.append('or a') + output.append('jr z, %s' % lbl) + output.append('ld a, h') + output.append('%s:' % lbl) output.append('push af') - REQUIRES.add('and8.asm') + # REQUIRES.add('and8.asm') return output @@ -782,7 +787,6 @@ def _band8(ins): output = _8bit_oper(op1, op2) output.append('and h') output.append('push af') - REQUIRES.add('and8.asm') return output diff --git a/arch/zx48k/backend/__f16.py b/arch/zx48k/backend/__f16.py index 6090edcbd..668c6f0ce 100644 --- a/arch/zx48k/backend/__f16.py +++ b/arch/zx48k/backend/__f16.py @@ -304,11 +304,11 @@ def _modf16(ins): op1, op2 = tuple(ins.quad[2:]) if is_float(op2) and float(op2) == 1: - output = _f16_oper(op1) - output.append('ld hl, 0') - output.append('push hl') - output.append('push hl') - return output + output = _f16_oper(op1) + output.append('ld hl, 0') + output.append('push hl') + output.append('push hl') + return output rev = not is_float(op1) and op1[0] != 't' and op2[0] == 't' diff --git a/arch/zx48k/backend/__init__.py b/arch/zx48k/backend/__init__.py index fe84af6ee..136d3e0f0 100644 --- a/arch/zx48k/backend/__init__.py +++ b/arch/zx48k/backend/__init__.py @@ -85,6 +85,7 @@ # External functions from ..optimizer import oper, inst, condition, HI16, LO16, is_16bit_idx_register from api.config import OPTIONS +import api.fp __all__ = [ '_fpop', @@ -119,9 +120,19 @@ OPT19 = True OPT21 = True OPT22 = True +OPT23 = True +OPT24 = True +OPT25 = True +OPT26 = True +OPT27 = True +OPT28 = True +OPT29 = True +OPT30 = True +OPT31 = True +OPT32 = True # Label RegExp -RE_LABEL = re.compile('^[ \t]*[a-zA-Z_][_a-zA-Z\d]*:') +RE_LABEL = re.compile(r'^[ \t]*[a-zA-Z_][_a-zA-Z\d]*:') # (ix +/- ...) regexp RE_IX_IDX = re.compile(r'^\([ \t]*ix[ \t]*[-+][ \t]*.+\)$') @@ -200,6 +211,8 @@ def init(): OPTIONS.add_option('heap_start_label', str, 'ZXBASIC_MEM_HEAP') # Labels for HEAP SIZE (might not be used if not needed) OPTIONS.add_option('heap_size_label', str, 'ZXBASIC_HEAP_SIZE') + # Flag for headerless mode (No prologue / epilogue) + OPTIONS.add_option('headerless', bool, False) def new_ASMID(): @@ -401,6 +414,9 @@ def _end(ins): FLAG_end_emitted = True output.append('%s:' % END_LABEL) + if OPTIONS.headerless.value: + return output + ['ret'] + output.append('di') output.append('ld hl, (%s)' % CALL_BACK) output.append('ld sp, hl') @@ -429,6 +445,45 @@ def _deflabel(ins): return ['%s EQU %s' % (str(ins.quad[1]), str(ins.quad[2]))] +def _data(ins): + """ Defines a data item (binary). + It's just a constant expression to be converted do binary data "as is" + + 1st parameter is the type-size (u8 or i8 for byte, u16 or i16 for word, etc) + 2nd parameter is the list of expressions. All of them will be converted to the + type required. + """ + output = [] + t = ins.quad[1] + q = eval(ins.quad[2]) + + if t in ('i8', 'u8'): + size = 'B' + elif t in ('i16', 'u16'): + size = 'W' + elif t in ('i32', 'u32'): + size = 'W' + z = list() + for expr in ins.quad[2]: + z.extend(['(%s) & 0xFFFF' % expr, '(%s) >> 16' % expr]) + q = z + elif t == 'str': + size = "B" + q = ['"%s"' % x.replace('"', '""') for x in q] + elif t == 'f': + dat_ = [api.fp.immediate_float(float(x)) for x in q] + for x in dat_: + output.extend(['DEFB %s' % x[0], 'DEFW %s, %s' % (x[1], x[2])]) + return output + else: + raise InvalidIC(ins.quad, 'Unimplemented data size %s for %s' % (t, q)) + + for x in q: + output.append('DEF%s %s' % (size, x)) + + return output + + def _var(ins): """ Defines a memory variable. """ @@ -555,21 +610,10 @@ def _lvard(ins): def _out(ins): """ Translates OUT to asm. """ - output = [] - - value = ins.quad[2] - try: - value = int(value) & 255 # Converted to byte - output.append('ld a, %i' % value) - except ValueError: - output.append('pop af') - - try: - port = int(ins.quad[1]) & 0xFFFF # Converted to word - output.append('ld bc, %i' % port) - except ValueError: - output.append('pop bc') - + output = _8bit_oper(ins.quad[2]) + output.extend(_16bit_oper(ins.quad[1])) + output.append('ld b, h') + output.append('ld c, l') output.append('out (c), a') return output @@ -578,14 +622,9 @@ def _out(ins): def _in(ins): """ Translates IN to asm. """ - output = [] - - try: - port = int(ins.quad[1]) & 0xFFFF # Converted to word - output.append('ld bc, %i' % port) - except ValueError: - output.append('pop bc') - + output = _16bit_oper(ins.quad[1]) + output.append('ld b, h') + output.append('ld c, l') output.append('in a, (c)') output.append('push af') @@ -1336,6 +1375,7 @@ def _ret8(ins): """ Returns from a procedure / function an 8bits value """ output = _8bit_oper(ins.quad[1]) + output.append('#pragma opt require a') output.append('jp %s' % str(ins.quad[2])) return output @@ -1344,6 +1384,7 @@ def _ret16(ins): """ Returns from a procedure / function a 16bits value """ output = _16bit_oper(ins.quad[1]) + output.append('#pragma opt require hl') output.append('jp %s' % str(ins.quad[2])) return output @@ -1352,6 +1393,7 @@ def _ret32(ins): """ Returns from a procedure / function a 32bits value (even Fixed point) """ output = _32bit_oper(ins.quad[1]) + output.append('#pragma opt require hl,de') output.append('jp %s' % str(ins.quad[2])) return output @@ -1360,6 +1402,7 @@ def _retf16(ins): """ Returns from a procedure / function a Fixed Point (32bits) value """ output = _f16_oper(ins.quad[1]) + output.append('#pragma opt require hl,de') output.append('jp %s' % str(ins.quad[2])) return output @@ -1368,6 +1411,7 @@ def _retf(ins): """ Returns from a procedure / function a Floating Point (40bits) value """ output = _float_oper(ins.quad[1]) + output.append('#pragma opt require a,bc,de') output.append('jp %s' % str(ins.quad[2])) return output @@ -1381,6 +1425,7 @@ def _retstr(ins): output.append('call __LOADSTR') REQUIRES.add('loadstr.asm') + output.append('#pragma opt require hl') output.append('jp %s' % str(ins.quad[2])) return output @@ -1696,7 +1741,7 @@ class Quad(object): def __init__(self, *args): """ Creates a quad-uple checking it has the current params. - Operatos should be passed as Quad('+', tSymbol, val1, val2) + Operators should be passed as Quad('+', tSymbol, val1, val2) """ if not args: raise InvalidIC('') @@ -1732,6 +1777,8 @@ def __str__(self): 'addstr': [3, _addstr], + 'data': [2, _data], + 'subi8': [3, _sub8], 'subu8': [3, _sub8], 'subi16': [3, _sub16], @@ -2116,7 +2163,9 @@ def __str__(self): # Program Start routine # ------------------------- def emit_start(): - output = [] + output = list() + if OPTIONS.headerless.value: + return output output.append('org %s' % OPTIONS.org.value) @@ -2313,13 +2362,23 @@ def output_join(output, new_chunk): """ changed = True and OPTIONS.optimization.value > 0 # Only enter here if -O0 was not set - while changed and len(new_chunk) > 0 and len(output) > 0: - a1 = output[-1] # Last output instruction - a2 = new_chunk[0] # Fist new output instruction + while changed and new_chunk: + if output: + a1 = output[-1] # Last output instruction + i1 = inst(a1) + o1 = oper(a1) + else: + a1 = i1 = o1 = None - i1 = inst(a1) + if len(output) > 1: + a0 = output[-2] + i0 = inst(a0) + o0 = oper(a0) + else: + a0 = i0 = o0 = None + + a2 = new_chunk[0] # Fist new output instruction i2 = inst(a2) - o1 = oper(a1) o2 = oper(a2) if OPT00 and i2[-1] == ':': @@ -2340,13 +2399,6 @@ def output_join(output, new_chunk): changed = True continue - if OPT02 and i1 == i2 == 'ld' and o1[0] == o2[1] and o2[0] == o1[1]: - # This and previous instruction are LD X, Y - # Ok, previous instruction is LD A, B and current is LD B, A. Remove this one. - new_chunk = new_chunk[1:] - changed = True - continue - if ( OPT03 and (i1 == 'sbc' and @@ -2417,9 +2469,6 @@ def output_join(output, new_chunk): # JP !, OTHER # LABEL: if OPT17 and len(output) > 1: - a0 = output[-2] - i0 = inst(a0) - o0 = oper(a0) if i0 == i1 == 'jp' \ and i2[-1] == ':' \ and condition(a0) in {'c', 'nc', 'z', 'nz'} \ @@ -2466,8 +2515,6 @@ def output_join(output, new_chunk): # jp nz, __LABEL if OPT19 and i1 == 'sub' and '1' in o1 and i2 == 'jp' and len(output) > 1: c2 = condition(new_chunk[0]) - a0 = output[-2] - i0 = inst(a0) if c2 in {'c', 'nc'}: cond = 'z' if c2 == 'c' else 'nz' new_chunk[0] = 'jp %s, %s' % (cond, o2[0]) @@ -2496,9 +2543,6 @@ def output_join(output, new_chunk): # ld r, (ix +/- n) if OPT22 and len(output) > 1 and i1 == 'ld' and o1[0] in 'bcdehl' and o1[1] == 'a' and \ (i2, o2) == ('pop', ['af', 'sp']): - a0 = output[-2] - i0 = inst(a0) - o0 = oper(a0) if (i0, o0[:1]) == ('ld', ['a']) and RE_IX_IDX.match(o0[1]): output.pop() # Removes ld r, a output.pop() # Removes ld a, (ix + n) @@ -2506,6 +2550,192 @@ def output_join(output, new_chunk): changed = True continue + # Converts: + # ld hl, (NN) | ld hl, NN | pop hl + # ld b, h + # ld c, l + # in a, (c) + # Into: + # ld bc, (NN) | ld bc, NN | pop bc + # in a, (c) + if OPT23 and len(new_chunk) > 3 and inst(new_chunk[3]) == 'in': + ia = inst(new_chunk[1]) + oa = oper(new_chunk[1]) + ib = inst(new_chunk[2]) + ob = oper(new_chunk[2]) + if (ia, oa[0], oa[1], ib, ob[0], ob[1]) == ('ld', 'b', 'h', 'ld', 'c', 'l'): + ii = inst(new_chunk[0]) + oi = oper(new_chunk[0]) + if ii in ('pop', 'ld') and oi[0] == 'hl': + new_chunk[0] = ii + ' ' + 'bc' + (', %s' % oi[1] if ii == 'ld' else '') + new_chunk.pop(1) + new_chunk.pop(1) + changed = True + continue + + # Converts: + # ld hl, (NN) | ld hl, NN | pop hl + # ld b, h + # ld c, l + # out (c), a + # Into: + # ld bc, (NN) | ld bc, NN | pop bc + # out (c), a + if OPT23 and len(new_chunk) > 3 and inst(new_chunk[-1]) == 'out': + ia = inst(new_chunk[-3]) + oa = oper(new_chunk[-3]) + ib = inst(new_chunk[-2]) + ob = oper(new_chunk[-2]) + if (ia, oa[0], oa[1], ib, ob[0], ob[1]) == ('ld', 'b', 'h', 'ld', 'c', 'l'): + ii = inst(new_chunk[-4]) + oi = oper(new_chunk[-4]) + if ii in ('pop', 'ld') and oi[0] == 'hl': + new_chunk[-4] = ii + ' ' + 'bc' + (', %s' % oi[1] if ii == 'ld' else '') + new_chunk.pop(-2) + new_chunk.pop(-2) + changed = True + continue + + # Converts: + # or X | and X + # or a | and a + # Into: + # or X | and X + if OPT24 and i1 in ('and', 'or') and new_chunk[0] in ('or a', 'and a'): + new_chunk.pop(0) + changed = True + continue + + # Converts: + # ld h, X (X != A) + # ld a, Y + # or/and/cp/add/sub h + # Into: + # ld a, Y + # or/and/cp X + if OPT25 and \ + (i1 in ('cp', 'or', 'and') and o1[0] == 'h' or + i1 in ('sub', 'add', 'sbc', 'adc') and o1[1] == 'h') \ + and i0 == 'ld' and o0[0] == 'a' and len(output) > 2: + ii = inst(output[-3]) + oo = oper(output[-3]) + if i1 in ('add', 'adc', 'sbc'): + i1 = i1 + ' a,' + if ii == 'ld' and oo[0] == 'h' and oo[1] != 'a': + output[-1] = '{0} {1}'.format(i1, oo[1]) + output.pop(-3) + changed = True + continue + + # Converts: + # ld a, (nn) | ld a, (ix+N) + # inc/dec a + # ld (nn), a | ld (ix+N), a + # Into: + # ld hl, _n | + # inc/dec (hl) | inc/dec (ix+N) + if OPT26 and i1 in ('inc', 'dec') and o1[0] == 'a' and i0 == i2 == 'ld' and \ + (o0[0], o0[1]) == (o2[1], o2[0]) and o0[1][0] == '(': + new_chunk.pop(0) + if RE_IX_IDX.match(o0[1]): + output[-1] = '{0} {1}'.format(i1, o0[1]) + output.pop(-2) + else: + output[-1] = '{0} (hl)'.format(i1) + output[-2] = 'ld hl, {0}'.format(o0[1][1:-1]) + changed = True + continue + + # Converts: + # ld X, Y + # ld Y, X + # Into: + # ld X, Y + if OPT02 and i1 == i2 == 'ld' and o1[0] == o2[1] and o2[0] == o1[1]: + # This and previous instruction are LD X, Y + # Ok, previous instruction is LD A, B and current is LD B, A. Remove this one. + new_chunk = new_chunk[1:] + changed = True + continue + + # Converts: + # ld h, X + # or/and h + # Into: + # or/and X + if OPT27 and i1 == 'ld' and o1[0] == 'h' and i2 in ('and', 'or') and o2[0] == 'h': + output.pop() + new_chunk[0] = '{0} {1}'.format(i2, o1[1]) + changed = True + continue + + # Converts + # ld a, r|(ix+/-N)|(hl) + # ld h, a + # ld a, XXX | pop af + # Into: + # ld h, r|(ix+/-N)|(hl) + # ld a, XXX | pop af + if OPT28 and i1 == i0 == 'ld' and o0[0] == 'a' and \ + (o0[1] in ('a', 'b', 'c', 'd', 'e', 'h', 'l', '(hl)') or RE_IX_IDX.match(o0[1])) and \ + (o1[0], o1[1]) == ('h', 'a') and new_chunk and (new_chunk[0] == 'pop af' or + i2 == 'ld' and o2[0] == 'a'): + + output.pop() + output[-1] = 'ld h, {0}'.format(o0[1]) + changed = True + continue + + # Converts: + # cp 0 + # Into: + # or a + if OPT29 and i1 == 'cp' and o1[0] == '0': + output[-1] = 'or a' + changed = True + continue + + # Converts: + # or/and X + # jp c/nc XXX + # Into: + # /jp XXX + if OPT30 and i1 in ('and', 'or') and i2 == 'jp': + c = condition(new_chunk[0]) + if c in ('c', 'nc'): + output.pop() + if c == 'nc': + new_chunk[0] = 'jp {0}'.format(o2[0]) + else: + new_chunk.pop(0) + changed = True + continue + + # Converts + # jp XXX + # + # Into: + # jp XXX + if OPT31 and i1 == 'jp' and not condition(output[-1]) and i2 is not None and \ + i2[-1] != ':' and new_chunk[0] not in ASMS: + new_chunk.pop(0) + changed = True + continue + + # Converts: + # call __LOADSTR + # ld a, 1 + # call __PRINSTR + # Into: + # xor a + # call __PRINTSTR + if OPT32 and i0 == 'call' and o0[0] == '__LOADSTR' and i1 == 'ld' and tuple(o1) == ('a', '1') and \ + i2 == 'call' and o2[0] == '__PRINTSTR': + output.pop(-2) + output[-1] = 'xor a' + changed = True + continue + changed, new_chunk = optiblock(new_chunk) output.extend(new_chunk) diff --git a/arch/zx48k/optimizer.py b/arch/zx48k/optimizer.py index f82e9506a..cf0b68e7a 100644 --- a/arch/zx48k/optimizer.py +++ b/arch/zx48k/optimizer.py @@ -12,11 +12,13 @@ from api.errors import Error from api.config import OPTIONS from api.debug import __DEBUG__ +from api.utils import flatten_list from identityset import IdentitySet import asmlex import arch.zx48k.backend from collections import defaultdict +UNKNOWN_PREFIX = '*UNKNOWN_' END_PROGRAM_LABEL = '__END_PROGRAM' # Label for end program sys.setrecursionlimit(10000) @@ -42,13 +44,14 @@ 'bc', 'de', 'hl', 'sp', 'ix', 'iy', 'ixh', 'ixl', 'iyh', 'iyl', 'af', "af'", 'i', 'r'} -RE_NUMBER = re.compile('^([-+]?[0-9]+|$[A-Fa-f0-9]+|[0-9][A-Fa-f0-9]*[Hh]|%[01]+|[01]+[bB])$') +RE_NUMBER = re.compile(r'^([-+]?[0-9]+|$[A-Fa-f0-9]+|[0-9][A-Fa-f0-9]*[Hh]|%[01]+|[01]+[bB])$') RE_INDIR = re.compile(r'\([ \t]*[Ii][XxYy][ \t]*[-+][ \t]*[0-9]+[ \t]*\)') RE_IXIND = re.compile(r'[iI][xXyY]([-+][0-9]+)?') RE_LABEL = re.compile(r'^[ \t]*[_a-zA-Z][a-zA-Z\d]*:') -RE_INDIR16 = re.compile('r[ \t]*\([ \t]*([dD][eE])|([hH][lL])[ \t]*\)[ \t]*') -RE_OUTC = re.compile('[ \t]*\([ \t]*[cC]\)') -RE_ID = re.compile('[.a-zA-Z_][.a-zA-Z_0-9]*') +RE_INDIR16 = re.compile(r'[ \t]*\([ \t]*([dD][eE])|([hH][lL])[ \t]*\)[ \t]*') +RE_OUTC = re.compile(r'[ \t]*\([ \t]*[cC]\)') +RE_ID = re.compile(r'[.a-zA-Z_][.a-zA-Z_0-9]*') +RE_PRAGMA = re.compile(r'^#[ \t]?pragma[ \t]opt[ \t]') # Enabled Optimizations (this is useful for debugging) OPT00 = True @@ -78,6 +81,7 @@ OPT24 = True OPT25 = True OPT26 = True +OPT27 = True RAND_COUNT = 0 @@ -86,7 +90,7 @@ def new_tmp_val(): global RAND_COUNT RAND_COUNT += 1 - return '*UNKNOWN_{0}'.format(RAND_COUNT) + return '{0}{1}'.format(UNKNOWN_PREFIX, RAND_COUNT) def is_8bit_normal_register(x): @@ -142,12 +146,15 @@ def is_number(x): if x is None: return False - if isinstance(x, int) or isinstance(x, float): + if isinstance(x, (int, float)): return True + if isinstance(x, str) and x[0] == '(' and x[-1] == ')': + return False + try: tmp = eval(x, {}, {}) - if isinstance(tmp, int) or isinstance(tmp, float): + if isinstance(tmp, (int, float)): return True except: pass @@ -155,6 +162,10 @@ def is_number(x): return RE_NUMBER.match(str(x)) is not None +def is_unknown(x): + return x is None or x.startswith(UNKNOWN_PREFIX) + + def valnum(x): if not is_number(x): return None @@ -194,15 +205,22 @@ def oper(inst): elif I in {'push', 'pop', 'call'}: op.append('sp') # Sp is also affected by push, pop and call - elif I in {'or', 'and', 'xor', 'neg', 'cpl', 'rrca', 'rlca', 'rra', 'rla'}: - op.extend(['a', 'f', 'af']) + elif I in {'or', 'and', 'xor', 'neg', 'cpl', 'rrca', 'rlca'}: + op.append('a') + + elif I in {'rra', 'rla'}: + op.extend(['a', 'f']) elif I in ('rr', 'rl'): op.append('f') - elif I in {'add', 'adc', 'sub', 'sbc'}: + elif I in {'adc', 'sbc'}: + if len(op) == 1: + op = ['a', 'f'] + op + + elif I in {'add', 'sub'}: if len(op) == 1: - op = ['a', 'f'] + op + ['af'] + op = ['a'] + op elif I in {'ldd', 'ldi', 'lddr', 'ldir'}: op = ['hl', 'de', 'bc'] @@ -357,175 +375,144 @@ def reset(self): """ self.regs = {} self.stack = [] - self.mem_regs = defaultdict(set) # list of labels and registers using then - self.mem = {} # List of labels and their values + self.mem = defaultdict(new_tmp_val) # Dict of label -> value in memory for i in 'abcdefhl': - self.regs[i] = None # Initial unknown state - self.regs["%s'" % i] = None - - self.regs['ixh'] = None - self.regs['ixl'] = None - self.regs['iyh'] = None - self.regs['iyl'] = None - self.regs['sp'] = None - self.regs['r'] = None - self.regs['i'] = None - - self.regs['af'] = None - self.regs['bc'] = None - self.regs['de'] = None - self.regs['hl'] = None - - self.regs['ix'] = None - self.regs['iy'] = None - - self.regs["af'"] = None - self.regs["bc'"] = None - self.regs["de'"] = None - self.regs["hl'"] = None + self.regs[i] = new_tmp_val() # Initial unknown state + self.regs["%s'" % i] = new_tmp_val() + + self.regs['ixh'] = new_tmp_val() + self.regs['ixl'] = new_tmp_val() + self.regs['iyh'] = new_tmp_val() + self.regs['iyl'] = new_tmp_val() + self.regs['sp'] = new_tmp_val() + self.regs['r'] = new_tmp_val() + self.regs['i'] = new_tmp_val() + + self.regs['af'] = new_tmp_val() + self.regs['bc'] = new_tmp_val() + self.regs['de'] = new_tmp_val() + self.regs['hl'] = new_tmp_val() + + self.regs['ix'] = new_tmp_val() + self.regs['iy'] = new_tmp_val() + + self.regs["af'"] = new_tmp_val() + self.regs["bc'"] = new_tmp_val() + self.regs["de'"] = new_tmp_val() + self.regs["hl'"] = new_tmp_val() self._16bit = {'b': 'bc', 'c': 'bc', 'd': 'de', 'e': 'de', 'h': 'hl', 'l': 'hl', "b'": "bc'", "c'": "bc'", "d'": "de'", "e'": "de'", "h'": "hl'", "l'": "hl'", 'ixy': 'ix', 'ixl': 'ix', 'iyh': 'iy', 'iyl': 'iy', 'a': 'af', "a'": "af'", 'f': 'af', "f'": "af'"} - self.C = self.Z = self.P = self.S = None + self.reset_flags() - def set(self, r, val): - is_num = is_number(val) + def reset_flags(self): + """ Resets flags to an "unknown state" + """ + self.C = None + self.Z = None + self.P = None + self.S = None - if is_num and self.getv(r) == valnum(val) & 0xFFFF: - return # The register already contains it value + def set(self, r, val): + if val is None: + is_num = False + val = new_tmp_val() + else: + val = str(val) + is_num = is_number(val) + if is_num and self.getv(r) == valnum(val) & 0xFFFF: + return # The register already contains this value if r == '(sp)': if not self.stack: - self.stack = [None] + self.stack = [new_tmp_val()] self.stack[-1] = str(valnum(val) & 0xFFFF) if is_num else val return - if r[0] == '(': + if r[0] == '(': # (mem) <- r => store in memory address r = r[1:-1].strip() if not RE_ID.match(r): return # not an ID if r in self.mem and val == self.mem[r]: - return # the same value to the same pos does nothing... (strong assumption) - # Ok, destroys cached value of any register containing this variable if any - for r_ in self.mem_regs[r]: - self.regs[r_] = None - if r_ == 'f': - self.C = self.Z = self.P = self.S = None - old_set = self.mem_regs[r] - self.mem_regs[r] = set() - self.mem[r] = self.regs.get(val, None) - for r_ in old_set: - if r_ in self.mem_regs: - self.set('(%s)' % r_, None) - if val in self.regs and self.regs[val] is None: # is a register? - self.set(val, '(%s)' % r) # mark it again, because now register contains (label) value + return # the same value to the same pos does nothing... (strong assumption: NON-VOLATILE) + if val not in self.regs: + self.regs[val] = new_tmp_val() + self.mem[r] = self.regs[val] return - if val and val[0] == '(': - if RE_ID.match(val[1:-1]): - v_ = val[1:-1] + if val and val[0] == '(': # r <- (mem) + v_ = val[1:-1].strip() + if RE_ID.match(v_): if v_ in self.mem: - self.set(r, self.mem[v_]) - r_ = self._16bit[r] if is_8bit_register(r) else r - self.mem_regs[v_].add(r_) - self.mem_regs[v_].update(single_registers(r_)) + val = self.mem[v_] + else: + val = self.mem[v_] = new_tmp_val() else: - self.set(r, None) - return + val = new_tmp_val() if is_8bit_register(r): if is_register(val): - self.regs[r] = self.regs[val] - val = self.regs[val] + val = self.regs[r] = self.regs[val] else: if is_num: oldval = self.getv(r) val = str(valnum(val) & 0xFF) if val == oldval: # Does not change return - self.regs[r] = val - # This change will reset any value related to this register - for reg8 in list('abcdehl') + ['ixh', 'ixl', 'iyh', 'iyl', - "a'", "b'", "c'", "d'", "e'", "h'", "l'"]: - tmp = self.regs[reg8] - if tmp is None or is_number(tmp): - continue - - if tmp[0] == '(': # (de), (hl), (ix+...), ( - tmp = tmp[1:-1] - - if r in tmp: # if other register depended on this - self.set(reg8, None) # the cached info is deleted - - if r not in self._16bit.keys(): + if r not in self._16bit: return hl = self._16bit[r] + self.mem[hl] = new_tmp_val() # Changing a 16 bit regs means changing the content of its *memptr + if not is_num or not is_number(self.regs[hl]): - self.regs[hl] = None # unknown + self.regs[hl] = new_tmp_val() # unknown return val = int(val) if r in {'b', 'd', 'h', 'ixh', 'iyh', "b'", "d'", "h'"}: # high register self.regs[hl] = str((val << 8) + int(self.regs[LO16(hl)])) - return + else: + self.regs[hl] = str((self.regs[HI16(hl)] << 8) + val) - self.regs[hl] = str((self.regs[HI16(hl)] << 8) + val) return # a 16 bit reg self.regs[r] = val - if is_16bit_register(r): # sp register is not included. Special case + self.mem[r] = new_tmp_val() + if not is_num: - self.regs[LO16(r)] = self.regs[HI16(r)] = None + self.regs[LO16(r)] = new_tmp_val() + self.regs[HI16(r)] = new_tmp_val() else: val = valnum(val) - self.regs[LO16(r)] = val & 0xFF - self.regs[HI16(r)] = val >> 8 - - # This change will reset any value related to this register - for reg16 in {'bc', 'de', 'hl', "bc'", "de'", "hl'", 'ix', 'iy'}: - tmp = self.regs[reg16] - if tmp is None or is_number(tmp): - continue - - if self.regs[reg16] == r: # any register - self.regs[reg16] = None - self.set(LO16(reg16), None) # Recursively destroys any register - self.set(HI16(reg16), None) # Depending on this one + self.regs[LO16(r)] = str(val & 0xFF) + self.regs[HI16(r)] = str(val >> 8) - for reg8 in list('abcdehl') + ['ixh', 'ixl', 'iyh', 'iyl', - "a'", "b'", "c'", "d'", "e'", "h'", "l'"]: - tmp = self.regs[reg8] - if tmp is None or is_number(tmp): - continue - - if tmp[0] == '(': # (de), (hl), (ix+...), ( - tmp = tmp[0:2] - - if r[0] in tmp or r[1] in tmp: # if other register depended on this - self.set(reg8, None) # the cached info is deleted - # Flags ??? - # self.C = self.S = self.Z = self.P = None + if 'f' in r: + self.reset_flags() def get(self, r): """ Returns precomputed value of the given expression """ - if r[:1] == '(' and r[-1:] == ')' and r[1:-1] in self.mem: - return self.mem[r[1:-1]] + if r is None: + return None - r = r.lower() - if r == '(sp)' and len(self.stack): + if r.lower() == '(sp)' and self.stack: return self.stack[-1] + if r[:1] == '(': + return self.mem[r[1:-1]] + + r = r.lower() if is_number(r): return str(valnum(r)) @@ -538,12 +525,13 @@ def getv(self, r): """ Like the above, but returns the value. """ v = self.get(r) - if v is not None: + if not is_unknown(v): try: v = int(v) - except: + except ValueError: v = None - + else: + v = None return v def eq(self, r1, r2): @@ -559,7 +547,8 @@ def eq(self, r1, r2): def set_flag(self, val): if not is_number(val): - self.regs['f'] = self.C = self.S = self.Z = self.P = None + self.regs['f'] = new_tmp_val() + self.reset_flags() return self.set('f', val) @@ -572,104 +561,114 @@ def set_flag(self, val): def inc(self, r): """ Does inc on the register and precomputes flags """ - if not is_register(r): - self.set_flag(None) + self.set_flag(None) - if r[0] == '(': - for i in self.regs.keys(): - if self.regs[i] == r: - self.set(i, None) + if not is_register(r): + if r[0] == '(': # a memory position, basically: inc(hl) + r_ = r[1:-1].strip() + v_ = self.getv(self.mem.get(r_, None)) + if v_ is not None: + v_ = (v_ + 1) & 0xFF + self.mem[r_] = str(v_) + self.Z = int(v_ == 0) # HINT: This might be improved + else: + self.mem[r_] = new_tmp_val() return if self.getv(r) is not None: self.set(r, self.getv(r) + 1) - return - - self.set(r, None) + else: + self.set(r, None) def dec(self, r): """ Does dec on the register and precomputes flags """ - if not is_register(r): - self.set_flag(None) + self.set_flag(None) - if r[0] == '(': - for i in self.regs.keys(): - if self.regs[i] == r: - self.set(i, None) + if not is_register(r): + if r[0] == '(': # a memory position, basically: inc(hl) + r_ = r[1:-1].strip() + v_ = self.getv(self.mem.get(r_, None)) + if v_ is not None: + v_ = (v_ - 1) & 0xFF + self.mem[r_] = str(v_) + self.Z = int(v_ == 0) # HINT: This might be improved + else: + self.mem[r_] = new_tmp_val() return if self.getv(r) is not None: self.set(r, self.getv(r) - 1) - return - - self.set(r, None) + else: + self.set(r, None) def rrc(self, r): """ Does a ROTATION to the RIGHT |>> """ - if self.regs[r] is None or isinstance(self.regs[r], str): + if not is_number(self.regs[r]): self.set(r, None) self.set_flag(None) return - self.regs[r] = (self.regs[r] >> 1) | ((self.regs[r] & 1) << 7) + v_ = self.getv(self.regs[r]) & 0xFF + self.regs[r] = str((v_ >> 1) | ((v_ & 1) << 7)) def rr(self, r): """ Like the above, bus uses carry """ - if self.C is None or self.regs[r] is None or isinstance(self.regs[r], str): + if self.C is None or not is_number(self.regs[r]): self.set(r, None) self.set_flag(None) return self.rrc(r) tmp = self.C - self.C = self.regs[r] >> 7 - self.regs[r] = (self.regs[r] & 0x7F) | (tmp << 7) + v_ = self.getv(self.regs[r]) + self.C = v_ >> 7 + self.regs[r] = str((v_ & 0x7F) | (tmp << 7)) def rlc(self, r): """ Does a ROTATION to the LEFT <<| """ - if self.regs[r] is None or isinstance(self.regs[r], str): + if not is_number(self.regs[r]): self.set(r, None) self.set_flag(None) return - self.set(r, ((self.regs[r] << 1) & 0xFF) | ((self.regs[r] & 1) >> 7)) + v_ = self.getv(self.regs[r]) & 0xFF + self.set(r, ((v_ << 1) & 0xFF) | (v_ >> 7)) def rl(self, r): """ Like the above, bus uses carry """ - if self.C is None or self.regs[r] is None or isinstance(self.regs[r], str): + if self.C is None or not is_number(self.regs[r]): self.set(r, None) self.set_flag(None) return self.rlc(r) tmp = self.C - self.C = self.regs[r] & 1 - self.regs[r] = (self.regs[r] & 0xFE) | tmp + v_ = self.getv(self.regs[r]) + self.C = v_ & 1 + self.regs[r] = str((v_ & 0xFE) | tmp) def _is(self, r, val): """ True if value of r is val. """ - if not is_register(r): + if not is_register(r) or val is None: return False r = r.lower() - - if self.regs[r] is None: - return False - if is_register(val): - if self.regs[val] is None: - return False - - return self.regs[val] == self.regs[r] + return self.eq(r, val) if is_number(val): val = str(valnum(val)) + else: + val = str(val) + + if val[0] == '(': + val = self.mem[val[1:-1]] return self.regs[r] == val @@ -686,55 +685,27 @@ def op(self, i, o): return if i == 'push': - if self.regs['sp'] is not None: - if RE_IXIND.match(self.regs['sp']): - tmp = self.regs['sp'].lower() - - if tmp in ('ix', 'iy'): - tmp += '-2' - else: - tmp = tmp[:2] + "%+i" % (int(tmp[2:]) - 2) - - self.set('sp', tmp) - elif valnum(self.regs['sp']): - self.set('sp', self.regs['sp'] - 2) - else: - self.set('sp', None) - - self.stack += [self.regs[o[0]]] + if valnum(self.regs['sp']): + self.set('sp', (self.getv(self.regs['sp']) - 2) % 0xFFFF) + else: + self.set('sp', None) + self.stack.append(self.regs[o[0]]) return if i == 'pop': - if self.stack == []: - self.set(o[0], None) - return - - self.set(o[0], self.stack[-1]) - self.stack.pop() - return - - if i in ('inc', 'dec'): - r = o[0] - - if i == 'inc': - self.inc(r) + self.set(o[0], self.stack and self.stack.pop() or None) + if valnum(self.regs['sp']): + self.set('sp', (self.getv(self.regs['sp']) + 2) % 0xFFFF) else: - self.dec(r) - - if is_16bit_register(r): - for i, v in zip(self.regs.keys(), self.regs.values()): - if v == r: # Value == '(hl)' or (SP), (IX) ... - self.set(i, None) - # Since hl has changed, every (hl) instance must be deleted here. - - # inc/dec on 16bit regs does not affect flags - return + self.set('sp', None) + return - if self.getv(r) is None: - self.set_flag(None) - return + if i == 'inc': + self.inc(o[0]) + return - self.Z = int(self.getv(r)) == 0 + if i == 'dec': + self.dec(o[0]) return if i == 'rra': @@ -934,6 +905,12 @@ def __set_asm(self, value): asm = property(__get_asm, __set_asm) + def __str__(self): + return self.asm + + def __repr__(self): + return '{0}:{1}'.format(self.addr, str(self)) + @property def is_label(self): """ Returns whether the current addr @@ -1032,7 +1009,10 @@ def destroys(self): elif i in {'ccf', 'scf', 'bit', 'cp'}: res.add('f') elif i in {'or', 'and', 'xor', 'add', 'adc', 'sub', 'sbc'}: - res.update(single_registers(o[0])) + if len(o) > 1: + res.update(single_registers(o[0])) + else: + res.add('a') res.add('f') elif i in {'neg', 'cpl', 'daa', 'rra', 'rla', 'rrca', 'rlca', 'rrd', 'rld'}: res.update('a', 'f') @@ -1053,6 +1033,15 @@ def requires(self): if self.asm in arch.zx48k.backend.ASMS: return ALL_REGS + if self.inst == '#pragma': + tmp = self.__instr.split(' ')[1:] + if tmp[0] != 'opt': + return + if tmp[1] == 'require': + return set(flatten_list([single_registers(x.strip(', \t\r')) for x in tmp[2:]])) + + return set([]) + result = set([]) i = self.inst o = [x.lower() for x in self.opers] @@ -1573,19 +1562,39 @@ def is_used(self, regs, i, top=None): return result - def requires(self, i=0): + def safe_to_write(self, regs, i=0, end_=0): + """ Given a list of registers (8 or 16 bits) returns a list of them + that are safe to modify from the given index until the position given + which, if omitted, defaults to the end of the block. + :param regs: register or iterable of registers (8 or 16 bit one) + :param i: initial position of the block to examine + :param end_: final position to examine + :returns: registers safe to write + """ + if is_register(regs): + regs = set(single_registers(regs)) + else: + regs = set(single_registers(x) for x in regs) + return not regs.intersection(self.requires(i, end_)) + + def requires(self, i=0, end_=None): """ Returns a list of registers and variables this block requires. By default checks from the beginning (i = 0). + :param i: initial position of the block to examine + :param end_: final position to examine + :returns: registers safe to write """ - regs = ['a', 'b', 'c', 'd', 'e', 'h', 'l', 'i', 'ixh', 'ixl', 'iyh', 'iyl', 'sp'] - top = len(self) + if i < 0: + i = 0 + end_ = len(self) if end_ is None or end_ > len(self) else end_ + regs = {'a', 'b', 'c', 'd', 'e', 'f', 'h', 'l', 'i', 'ixh', 'ixl', 'iyh', 'iyl', 'sp'} result = [] - for ii in range(i, top): + for ii in range(i, end_): for r in self.mem[ii].requires: r = r.lower() if r in regs: - result += [r] + result.append(r) regs.remove(r) for r in self.mem[ii].destroys: @@ -1593,7 +1602,7 @@ def requires(self, i=0): if r in regs: regs.remove(r) - if regs == []: + if not regs: break return result @@ -1602,18 +1611,17 @@ def destroys(self, i=0): """ Returns a list of registers this block destroys By default checks from the beginning (i = 0). """ - regs = ['a', 'b', 'c', 'd', 'e', 'h', 'l', 'i', 'ixh', 'ixl', 'iyh', 'iyl', 'sp'] + regs = {'a', 'b', 'c', 'd', 'e', 'f', 'h', 'l', 'i', 'ixh', 'ixl', 'iyh', 'iyl', 'sp'} top = len(self) result = [] for ii in range(i, top): for r in self.mem[ii].destroys: if r in regs: - result += [r] + result.append(r) regs.remove(r) - break - if regs == []: + if not regs: break return result @@ -1682,9 +1690,14 @@ def optimize(self): continue for i in range(len(self)): - if self.mem[i].is_label: - # ignore labels - continue + try: + if self.mem[i].is_label: + # ignore labels + continue + except IndexError: + print(i) + print('\n'.join(str(x) for x in self.mem)) + raise i1 = self.mem[i].inst o1 = self.mem[i].opers @@ -1888,7 +1901,9 @@ def optimize(self): break if OPT16 and i > 0 and not self.mem[i - 1].is_label and i1 == 'pop' and \ - not self.mem[i - 1].affects([o1[0], 'sp']) and not self.mem[i - 1].needs([o1[0], 'sp']): + (not self.mem[i - 1].affects([o1[0], 'sp']) or + self.safe_to_write(o1[0], i + 1)) and \ + not self.mem[i - 1].needs([o1[0], 'sp']): # { ; POP X } => { POP X; } ; if inst does not uses X tmp = str(self.asm) self.swap(i - 1, i) @@ -2003,13 +2018,22 @@ def optimize(self): if changed: break - if OPT26 and i1 == i2 == 'ld' and (o1[0], o1[1], o2[0], o2[1]) == ('d', 'h', 'e', 'l') and not \ - self.is_used(['h', 'l'], i + 2): + if OPT26 and i1 == i2 == 'ld' and (o1[0], o1[1], o2[0], o2[1]) == ('d', 'h', 'e', 'l') and \ + not self.is_used(['h', 'l'], i + 2): self[i] = 'ex de, hl' self.pop(i + 1) changed = True break + if OPT27 and i1 in ('cp', 'or', 'and', 'add', 'adc', 'sub', 'sbc') and o1[-1] != 'a' and \ + not self.is_used(o1[-1], i + 1) and i0 == 'ld' and o0[0] == o1[-1] and \ + (o0[1] == '(hl)' or RE_IXIND.match(o0[1])): + template = '{0} %s{1}' % ('a, ' if i1 in ('add', 'adc', 'sbc') else '') + self[i] = template.format(i1, o0[1]) + self.pop(i - 1) + changed = True + break + regs.op(i1, o1) @@ -2130,18 +2154,6 @@ def partition_block(block): return result -def flatten_list(x): - result = [] - - for l in x: - if not isinstance(l, list): - result += [l] - else: - result += flatten_list(l) - - return result - - # --------------------------------------------------------------------------------------- def get_basic_blocks(bb): @@ -2343,7 +2355,7 @@ def optimize(initial_memory): cleanupmem(initial_memory) if OPTIONS.optimization.value <= 2: - return '\n'.join(initial_memory) + return '\n'.join(x for x in initial_memory if not RE_PRAGMA.match(x)) optimize_init() bb = BasicBlock(initial_memory) @@ -2372,4 +2384,5 @@ def optimize(initial_memory): if x.comes_from == [] and len([y for y in JUMP_LABELS if x is LABELS[y].basic_block]): x.ignored = True - return '\n'.join(flatten_list([x.asm for x in basic_blocks if not x.ignored])) + return '\n'.join([y for y in flatten_list([x.asm for x in basic_blocks if not x.ignored]) + if not RE_PRAGMA.match(y)]) diff --git a/arch/zx48k/translator.py b/arch/zx48k/translator.py index 3e26160b3..ea0fccbcd 100644 --- a/arch/zx48k/translator.py +++ b/arch/zx48k/translator.py @@ -3,6 +3,7 @@ import functools from collections import OrderedDict +from collections import namedtuple from api.constants import TYPE from api.constants import SCOPE @@ -40,6 +41,8 @@ 'VarTranslator', 'FunctionTranslator'] +JumpTable = namedtuple('JumpTable', ('label', 'addresses')) + class TranslatorVisitor(NodeVisitor): """ This visitor just adds the emit() method. @@ -62,11 +65,38 @@ class TranslatorVisitor(NodeVisitor): LOOPS = [] # Defined LOOPS STRING_LABELS = OrderedDict() + JUMP_TABLES = [] + + # Type code used in DATA + DATA_TYPES = { + 'str': 1, + 'i8': 2, + 'u8': 3, + 'i16': 4, + 'u16': 5, + 'i32': 6, + 'u32': 7, + 'f16': 8, + 'f': 9 + } @classmethod def reset(cls): cls.LOOPS = [] # Defined LOOPS cls.STRING_LABELS = OrderedDict() + cls.JUMP_TABLES = [] + + def add_string_label(self, str_): + """ Maps ("folds") the given string, returning an unique label ID. + This allows several constant labels to be initialized to the same address + thus saving memory space. + :param str_: the string to map + :return: the unique label ID + """ + if self.STRING_LABELS.get(str_, None) is None: + self.STRING_LABELS[str_] = backend.tmp_label() + + return self.STRING_LABELS[str_] @property def O_LEVEL(self): @@ -135,11 +165,46 @@ def visit_ATTR_TMP(self, node): ifile = ifile[:ifile.index('_')] backend.REQUIRES.add(ifile + '.asm') + # This function must be called before emit_strings + def emit_data_blocks(self): + if not gl.DATA_IS_USED: + return # nothing to do + + for label_, datas in gl.DATAS: + self.emit('label', label_) + for d in datas: + if isinstance(d, symbols.FUNCDECL): + type_ = '%02Xh' % (self.DATA_TYPES[self.TSUFFIX(d.type_)] | 0x80) + self.emit('data', self.TSUFFIX(TYPE.byte_), [type_]) + self.emit('data', self.TSUFFIX(gl.PTR_TYPE), [d.mangled]) + continue + + self.emit('data', self.TSUFFIX(TYPE.byte_), [self.DATA_TYPES[self.TSUFFIX(d.value.type_)]]) + if d.value.type_ == self.TYPE(TYPE.string): + lbl = self.add_string_label(d.value.value) + self.emit('data', self.TSUFFIX(api.global_.PTR_TYPE), [lbl]) + elif d.value.type_ == self.TYPE(TYPE.fixed): # Convert to bytes + bytes_ = 0xFFFFFFFF & int(d.value.value * 2 ** 16) + self.emit('data', self.TSUFFIX(TYPE.uinteger), + ['0x%04X' % (bytes_ & 0xFFFF), '0x%04X' % (bytes_ >> 16)]) + else: + self.emit('data', self.TSUFFIX(d.value.type_), [self.traverse_const(d.value)]) + + if not gl.DATAS: # The above loop was not executed, because there's no data + self.emit('label', '__DATA__0') + + self.emit('vard', '__DATA__END', ['00']) + def emit_strings(self): for str_, label_ in self.STRING_LABELS.items(): l = '%04X' % (len(str_) & 0xFFFF) # TODO: Universalize for any arch self.emit('vard', label_, [l] + ['%02X' % ord(x) for x in str_]) + def emit_jump_tables(self): + for table_ in self.JUMP_TABLES: + self.emit('vard', table_.label, [str(len(table_.addresses))] + + ['##' + x.mangled for x in table_.addresses]) + def _visit(self, node): self.norm_attr() if isinstance(node, Symbol): @@ -223,6 +288,9 @@ def traverse_const(node): if node.token == 'CONST': return Translator.traverse_const(node.expr) + if node.token == 'ARRAYACCESS': + return '({} + {})'.format(node.entry.mangled, node.offset) + raise InvalidCONSTexpr(node) @staticmethod @@ -252,16 +320,13 @@ def visit_NUMBER(self, node): def visit_STRING(self, node): __DEBUG__('STRING ' + str(node)) - if self.STRING_LABELS.get(node.value, None) is None: - self.STRING_LABELS[node.value] = backend.tmp_label() - - node.t = '#' + self.STRING_LABELS[node.value] + node.t = '#' + self.add_string_label(node.value) yield node.t def visit_END(self, node): - arg = (yield node.children[0]) + yield node.children[0] __DEBUG__('END') - self.emit('end', arg) + self.emit('end', node.children[0].t) def visit_ERROR(self, node): # Raises an error @@ -398,6 +463,11 @@ def visit_ARGLIST(self, node): for i in range(len(node) - 1, -1, -1): # visit in reverse order yield node[i] + if isinstance(node.parent, symbols.ARRAYACCESS) and OPTIONS.arrayCheck.value: + upper = node.parent.entry.bounds[i].upper + lower = node.parent.entry.bounds[i].lower + self.emit('paramu16', upper - lower) + def visit_ARGUMENT(self, node): if not node.byref: suffix = self.TSUFFIX(node.type_) @@ -437,11 +507,6 @@ def visit_ARRAYLOAD(self, node): if node.offset is None: yield node.args - if OPTIONS.arrayCheck.value: - upper = node.entry.bounds[0].upper - lower = node.entry.bounds[0].lower - self.emit('paramu16', upper - lower) - if scope == SCOPE.global_: self.emit('aload' + self.TSUFFIX(node.type_), node.entry.t, node.entry.mangled) elif scope == SCOPE.parameter: @@ -537,12 +602,54 @@ def visit_LETSUBSTR(self, node): self.emit('call', '__LETSUBSTR', 0) backend.REQUIRES.add('letsubstr.asm') + 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 + self.emit('paramstr', expr.t) + + if expr.token != 'STRING' and (expr.token != 'VAR' or expr.mangled[0] != '_'): + self.emit('paramu8', 1) # If the argument is not a variable, it must be freed + else: + self.emit('paramu8', 0) + + yield node.children[1] + self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.children[1].t) + yield node.children[2] + self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.children[2].t) + + node_ = node.children[0] + scope = node_.scope + entry = node_.entry + suffix = self.TSUFFIX(gl.PTR_TYPE) + + # Address of an array element. + if node_.offset is None: + yield node_ + if scope == SCOPE.global_: + self.emit('aload' + suffix, node_.t, entry.mangled) + elif scope == 'parameter': + self.emit('paloadstr' + suffix, node_.t, entry.offset) + elif scope == 'local': + self.emit('paloadstr' + suffix, node_.t, -entry.offset) + else: + offset = node_.offset + if scope == SCOPE.global_: + self.emit('load' + suffix, entry.t, '%s + %i' % (entry.mangled, offset)) + elif scope == SCOPE.parameter: + self.emit('pload' + suffix, node_.t, entry.offset - offset) + elif scope == SCOPE.local: + self.emit('pload' + suffix, node_.t, -(entry.offset - offset)) + + self.emit('fparam' + suffix, node.children[0].t) + self.emit('call', '__LETSUBSTR', 0) + backend.REQUIRES.add('letsubstr.asm') + def visit_ARRAYACCESS(self, node): yield node.arglist - if OPTIONS.arrayCheck.value: - self.emit('param' + self.TSUFFIX(gl.BOUND_TYPE), len(node.entry.bounds)) - def visit_STRSLICE(self, node): yield node.string if node.string.token == 'STRING' or \ @@ -574,6 +681,46 @@ def visit_FUNCCALL(self, node): self.emit('call', node.entry.mangled, node.entry.size) + def visit_RESTORE(self, node): + if not gl.DATA_IS_USED: + return # If no READ is used, ignore all DATA related statements + lbl = gl.DATA_LABELS[node.args[0].name] + self.emit('fparam' + self.TSUFFIX(node.args[0].type_), '#' + lbl) + self.emit('call', '__RESTORE', 0) + backend.REQUIRES.add('read_restore.asm') + + def visit_READ(self, node): + self.emit('fparamu8', '#' + str(self.DATA_TYPES[self.TSUFFIX(node.args[0].type_)])) + self.emit('call', '__READ', node.args[0].type_.size) + + if isinstance(node.args[0], symbols.ARRAYACCESS): + arr = node.args[0] + t = api.global_.optemps.new_t() + scope = arr.scope + suf = self.TSUFFIX(arr.type_) + + if arr.offset is None: + yield arr + + if scope == SCOPE.global_: + self.emit('astore' + suf, arr.entry.mangled, t) + elif scope == SCOPE.parameter: + self.emit('pastore' + suf, arr.entry.offset, t) + elif scope == SCOPE.local: + self.emit('pastore' + suf, -arr.entry.offset, t) + else: + name = arr.entry.mangled + if scope == SCOPE.global_: + self.emit('store' + suf, '%s + %i' % (name, arr.offset), t) + elif scope == SCOPE.parameter: + self.emit('pstore' + suf, arr.entry.offset - arr.offset, t) + elif scope == SCOPE.local: + self.emit('pstore' + suf, -(arr.entry.offset - arr.offset), t) + + else: + self.emit_var_assign(node.args[0], t=api.global_.optemps.new_t()) + backend.REQUIRES.add('read_restore.asm') + # region Control Flow Sentences # ----------------------------------------------------------------------------------------------------- # Control Flow Compound sentences FOR, IF, WHILE, DO UNTIL... @@ -693,7 +840,6 @@ def visit_FOR(self, node): self.emit('label', end_loop) self.LOOPS.pop() - # del loop_label_start, end_loop, loop_label_gt, loop_label_lt, loop_body, loop_continue def visit_GOTO(self, node): self.emit('jump', node.children[0].mangled) @@ -701,13 +847,33 @@ def visit_GOTO(self, node): def visit_GOSUB(self, node): self.emit('call', node.children[0].mangled, 0) + def visit_ON_GOTO(self, node): + table_label = backend.tmp_label() + self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), '#' + table_label) + yield node.children[0] + self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) + self.emit('call', '__ON_GOTO', 0) + self.JUMP_TABLES.append(JumpTable(table_label, node.children[1:])) + backend.REQUIRES.add('ongoto.asm') + + def visit_ON_GOSUB(self, node): + table_label = backend.tmp_label() + self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), '#' + table_label) + yield node.children[0] + self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) + self.emit('call', '__ON_GOSUB', 0) + self.JUMP_TABLES.append(JumpTable(table_label, node.children[1:])) + backend.REQUIRES.add('ongoto.asm') + def visit_CHKBREAK(self, node): if self.PREV_TOKEN != node.token: + self.emit('inline', 'push hl', node.children[0].t) self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), node.children[0].t) self.emit('call', 'CHECK_BREAK', 0) backend.REQUIRES.add('break.asm') def visit_IF(self, node): + assert 1 < len(node.children) < 4, 'IF nodes: %i' % len(node.children) yield node.children[0] if_label_else = backend.tmp_label() if_label_endif = backend.tmp_label() @@ -990,14 +1156,12 @@ def visit_ASM(self, node): # -------------------------------------- # Helpers # -------------------------------------- - def emit_let_left_part(self, node, t=None): - var = node.children[0] - expr = node.children[1] + def emit_var_assign(self, var, t): + """ Emits code for storing a value into a variable + :param var: variable (node) to be updated + :param t: the value to emmit (e.g. a _label, a const, a tN...) + """ p = '*' if var.byref else '' # Indirection prefix - - if t is None: - t = expr.t # TODO: Check - if self.O_LEVEL > 1 and not var.accessed: return @@ -1013,6 +1177,15 @@ def emit_let_left_part(self, node, t=None): var.offset -= 1 + 2 * var.alias.count self.emit('pstore' + self.TSUFFIX(var.type_), p + str(-var.offset), t) + def emit_let_left_part(self, node, t=None): + var = node.children[0] + expr = node.children[1] + + if t is None: + t = expr.t # TODO: Check + + return self.emit_var_assign(var, t) + # endregion # region [Static Methods] @@ -1045,9 +1218,9 @@ def default_value(cls, type_, expr): # TODO: This function must be moved to api """ assert isinstance(type_, symbols.TYPE) assert type_.is_basic - assert isinstance(expr, symbols.NUMBER) or isinstance(expr, symbols.CONST) + assert check.is_static(expr) - if isinstance(expr, symbols.CONST): # a constant expression like @label + 1 + if isinstance(expr, (symbols.CONST, symbols.VAR)): # a constant expression like @label + 1 if type_ in (cls.TYPE(TYPE.float_), cls.TYPE(TYPE.string)): syntax_error(expr.lineno, "Can't convert non-numeric value to {0} at compile time".format(type_.name)) return [''] diff --git a/asmlex.py b/asmlex.py index ae6e734d3..f50c65c49 100755 --- a/asmlex.py +++ b/asmlex.py @@ -16,7 +16,7 @@ from api.config import OPTIONS from api.errmsg import syntax_error -_tokens = ('STRING', 'NEWLINE', 'LABEL', +_tokens = ('STRING', 'NEWLINE', 'LABEL', 'CO', 'ID', 'COMMA', 'PLUS', 'MINUS', 'LP', 'RP', 'LPP', 'RPP', 'MUL', 'DIV', 'POW', 'MOD', 'UMINUS', 'APO', 'INTEGER', 'ADDR', 'LSHIFT', 'RSHIFT', 'BAND', 'BOR', 'BXOR' @@ -109,8 +109,7 @@ 'local': 'LOCAL', 'end': 'END', 'incbin': 'INCBIN', - 'namespace': 'NAMESPACE', - 'default': 'DEFAULT' + 'namespace': 'NAMESPACE' } regs8 = {'a': 'A', @@ -245,12 +244,12 @@ def t_INITIAL_preproc_INTEGER(self, t): return t def t_INITIAL_ID(self, t): - r'[.]?[_a-zA-Z]([.]?[_a-zA-Z0-9]+)*[:]?' # Any identifier + r'[._a-zA-Z][._a-zA-Z0-9]*([ \t]*[:])?' # Any identifier tmp = t.value # Saves original value if tmp[-1] == ':': t.type = 'LABEL' - t.value = tmp[:-1] + t.value = tmp[:-1].strip() return t t.value = tmp.upper() # Convert it to uppercase, since our internal tables uses uppercase @@ -351,15 +350,14 @@ def t_APO(self, t): r"'" return t - def t_INITIAL_preproc_STRING(self, t): - r'"[^"]*"' # a doubled quoted string - t.value = t.value[1:-1] # Remove quotes + def t_CO(self, t): + r":" return t - def t_INITIAL_preproc_error(self, t): - """ error handling rule - """ - syntax_error(t.lexer.lineno, "illegal character '%s'" % t.value[0]) + def t_INITIAL_preproc_STRING(self, t): + r'"(""|[^"])*"' # a doubled quoted string + t.value = t.value[1:-1].replace('""', '"') # Remove quotes + return t def t_INITIAL_preproc_CONTINUE(self, t): r'\\\r?\n' @@ -372,7 +370,6 @@ def t_COMMENT(self, t): def t_INITIAL_preproc_NEWLINE(self, t): r'\r?\n' - t.lexer.lineno += 1 t.lexer.begin('INITIAL') return t @@ -383,7 +380,15 @@ def t_INITIAL_SHARP(self, t): if self.find_column(t) == 1: t.lexer.begin('preproc') else: - syntax_error(t.lexer.lineno, "illegal character '%s'" % t.value[0]) + self.t_INITIAL_preproc_error(t) + + def t_INITIAL_preproc_ERROR(self, t): + r'.' + self.t_INITIAL_preproc_error(t) + + def t_INITIAL_preproc_error(self, t): + # error handling rule + syntax_error(t.lexer.lineno, "illegal character '%s'" % t.value[0]) def __init__(self): """ Creates a new GLOBAL lexer instance diff --git a/asmparse.py b/asmparse.py index 8b6438cfc..045a66c58 100755 --- a/asmparse.py +++ b/asmparse.py @@ -12,6 +12,7 @@ # ---------------------------------------------------------------------- import os +import re import asmlex import ply.yacc as yacc @@ -23,6 +24,8 @@ from api.errmsg import syntax_error as error from api.errmsg import warning from api import global_ as gl +import api.utils +import zxbpp LEXER = asmlex.Lexer() @@ -30,9 +33,9 @@ INITS = [] MEMORY = None # Memory for instructions (Will be initialized with a Memory() instance) AUTORUN_ADDR = None # Where to start the execution automatically +RE_DOTS = re.compile(r'\.+') -REGS16 = ('BC', 'DE', 'HL', 'SP', 'IX', 'IY') # 16 Bits registers -REGS8 = ('A', 'B', 'C', 'D', 'E', 'H', 'L', 'IXh', 'IXl', 'IYh', 'IYl') +REGS16 = {'BC', 'DE', 'HL', 'SP', 'IX', 'IY'} # 16 Bits registers precedence = ( ('left', 'RSHIFT', 'LSHIFT', 'BAND', 'BOR', 'BXOR'), @@ -44,7 +47,20 @@ MAX_MEM = 65535 # Max memory limit DOT = '.' # NAMESPACE separator -NAMESPACE = '' # Current namespace (defaults to ''). It's a prefix added to each global label +GLOBAL_NAMESPACE = DOT +NAMESPACE = GLOBAL_NAMESPACE # Current namespace (defaults to ''). It's a prefix added to each global label + + +def normalize_namespace(namespace): + """ Given a namespace (e.g. '.' or 'mynamespace'), + returns it in normalized form. That is: + - always prefixed with a dot + - no trailing dots + - any double dots are converted to single dot (..my..namespace => .my.namespace) + - one or more dots (e.g. '.', '..', '...') are converted to '.' (Global namespace) + """ + namespace = (DOT + DOT.join(RE_DOTS.split(namespace))).rstrip(DOT) + DOT + return namespace def init(): @@ -61,8 +77,9 @@ def init(): INITS = [] MEMORY = None # Memory for instructions (Will be initialized with a Memory() instance) AUTORUN_ADDR = None # Where to start the execution automatically - NAMESPACE = '' # Current namespace (defaults to ''). It's a prefix added to each global label + NAMESPACE = GLOBAL_NAMESPACE # Current namespace (defaults to ''). It's a prefix added to each global label gl.has_errors = 0 + gl.error_msg_cache.clear() class Asm(AsmInstruction): @@ -149,6 +166,9 @@ def argval(self): return tuple([x.eval() if isinstance(x, Expr) else x for x in self.arg]) self.arg = tuple([x if not isinstance(x, Expr) else x.eval() for x in self.arg]) + if gl.has_errors: + return [None] + if self.asm.split(' ')[0] in ('JR', 'DJNZ'): # A relative jump? if self.arg[0] < -128 or self.arg[0] > 127: error(self.lineno, 'Relative jump out of range') @@ -328,10 +348,7 @@ def resolve(self, lineno): @property def name(self): - if self.namespace is not None: - return self.namespace + self._name - - return self.current_namespace + self._name + return self._name class Memory(object): @@ -364,6 +381,23 @@ def set_org(self, value, lineno): self.index = self.ORG = value + @staticmethod + def id_name(label, namespace=None): + """ Given a name and a namespace, resolves + returns the name as namespace + '.' + name. If namespace + is none, the current NAMESPACE is used + """ + if not label.startswith(DOT): + if namespace is None: + namespace = NAMESPACE + ex_label = namespace + label # The mangled namespace.labelname label + else: + if namespace is None: + namespace = GLOBAL_NAMESPACE # Global namespace + ex_label = label + + return ex_label, namespace + @property def org(self): """ Returns current ORG index @@ -416,17 +450,21 @@ def set_memory_slot(self): self.orgs[self.org] = () # Declares an empty memory slot if not already done self.memory_bytes[self.org] = () # Declares an empty memory slot if not already done - def add_instruction(self, asm): + def add_instruction(self, instr): """ This will insert an asm instruction at the current memory position in a t-uple as (mnemonic, params). It will also insert the opcodes at the memory_bytes """ + if gl.has_errors: + return + + __DEBUG__('%04Xh [%04Xh] ASM: %s' % (self.org, self.org - self.ORG, instr.asm)) self.set_memory_slot() - self.orgs[self.org] += (asm,) + self.orgs[self.org] += (instr,) - for byte in asm.bytes(): - self.__set_byte(byte, asm.lineno) + for byte in instr.bytes(): + self.__set_byte(byte, instr.lineno) def dump(self): """ Returns a tuple containing code ORG, and a list of OUTPUT @@ -474,14 +512,7 @@ def declare_label(self, label, lineno, value=None, local=False, namespace=None): Exits with error if label already set, otherwise return the label object """ - if label[0] != DOT: - if namespace is None: - namespace = NAMESPACE - ex_label = namespace + label # The mangled namespace.labelname label - else: - if namespace is None: - namespace = '' # Global namespace - ex_label = label = label[len(DOT):] + ex_label, namespace = Memory.id_name(label, namespace) is_address = value is None if value is None: @@ -491,10 +522,9 @@ def declare_label(self, label, lineno, value=None, local=False, namespace=None): self.local_labels[-1][ex_label].define(value, lineno) self.local_labels[-1][ex_label].is_address = is_address else: - self.local_labels[-1][ex_label] = Label(label, lineno, value, local, namespace, is_address) + self.local_labels[-1][ex_label] = Label(ex_label, lineno, value, local, namespace, is_address) self.set_memory_slot() - self.memory_bytes[self.org] += ('%s:' % ex_label,) return self.local_labels[-1][ex_label] @@ -504,19 +534,14 @@ def get_label(self, label, lineno): """ global NAMESPACE - if label[0] != DOT: - ex_label = NAMESPACE + label # expanded name - namespace = NAMESPACE - else: - ex_label = label = label[len(DOT):] - namespace = '' + ex_label, namespace = Memory.id_name(label) for i in range(len(self.local_labels) - 1, -1, -1): # Downstep result = self.local_labels[i].get(ex_label, None) if result is not None: return result - result = Label(label, lineno, namespace=namespace) + result = Label(ex_label, lineno, namespace=namespace) self.local_labels[-1][ex_label] = result # HINT: no namespace return result @@ -528,16 +553,13 @@ def set_label(self, label, lineno, local=False): The resulting label is returned. """ - if label[0] != DOT: - ex_label = NAMESPACE + label # expanded name - else: - ex_label = label = label[len(DOT):] + ex_label, namespace = Memory.id_name(label) if ex_label in self.local_labels[-1].keys(): result = self.local_labels[-1][ex_label] result.lineno = lineno else: - result = self.local_labels[-1][ex_label] = Label(label, lineno, namespace=NAMESPACE) + result = self.local_labels[-1][ex_label] = Label(ex_label, lineno, namespace=NAMESPACE) if result.local == local: warning(lineno, "label '%s' already declared as LOCAL" % label) @@ -579,16 +601,14 @@ def p_program(p): """ program : line """ if p[1] is not None: - __DEBUG__('%04Xh [%04Xh] ASM: %s' % (MEMORY.org, MEMORY.org - MEMORY.ORG, p[1].asm)) - MEMORY.add_instruction(p[1]) + [MEMORY.add_instruction(x) for x in p[1] if isinstance(x, Asm)] def p_program_line(p): """ program : program line """ if p[2] is not None: - __DEBUG__('%04Xh [%04Xh] ASM: %s' % (MEMORY.org, MEMORY.org - MEMORY.ORG, p[2].asm)) - MEMORY.add_instruction(p[2]) + [MEMORY.add_instruction(x) for x in p[2] if isinstance(x, Asm)] def p_def_label(p): @@ -597,38 +617,40 @@ def p_def_label(p): """ p[0] = None __DEBUG__("Declaring '%s%s' in %i" % (NAMESPACE, p[1], p.lineno(1))) - MEMORY.declare_label(p[1], p.lineno(1), p[3]) -def p_line_label(p): - """ line : LABEL NEWLINE +def p_line_label_asm(p): + """ line : LABEL asms NEWLINE """ - p[0] = None # Nothing to append + p[0] = p[2] __DEBUG__("Declaring '%s%s' (value %04Xh) in %i" % (NAMESPACE, p[1], MEMORY.org, p.lineno(1))) - MEMORY.declare_label(p[1], p.lineno(1)) -def p_line_label_asm(p): - """ line : LABEL asm NEWLINE +def p_line_asm(p): + """ line : asms NEWLINE """ - p[0] = p[2] - __DEBUG__("Declaring '%s' (value %04Xh) in %i" % (p[1], MEMORY.org, p.lineno(1))) + p[0] = p[1] - MEMORY.declare_label(p[1], p.lineno(1)) +def p_asms_empty(p): + """ asms : + """ + p[0] = [] -def p_line_asm(p): - """ line : asm NEWLINE + +def p_asms_asm(p): + """ asms : asm """ - p[0] = p[1] + p[0] = [p[1]] -def p_line_newline(p): - """ line : NEWLINE +def p_asms_asms_asm(p): + """ asms : asms CO asm """ - p[0] = None + p[1].append(p[3]) + p[0] = p[1] def p_asm_ld8(p): @@ -801,17 +823,12 @@ def p_org(p): def p_namespace(p): - """ asm : NAMESPACE DEFAULT - | NAMESPACE ID + """ asm : NAMESPACE ID """ global NAMESPACE - if p[2] != 'DEFAULT': - NAMESPACE = p[2] + '.' - else: - NAMESPACE = '' - - __DEBUG__('Setting namespace to ' + NAMESPACE, level=1) + NAMESPACE = normalize_namespace(p[2]) + __DEBUG__('Setting namespace to ' + (NAMESPACE.rstrip(DOT) or DOT), level=1) def p_align(p): @@ -830,7 +847,12 @@ def p_incbin(p): """ asm : INCBIN STRING """ try: - filecontent = open(p[2], 'rb').read() + fname = zxbpp.search_filename(p[2], p.lineno(2), local_first=True) + if not fname: + p[0] = None + return + with 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 @@ -1363,7 +1385,7 @@ def p_expr_addr(p): # Some preprocessor directives def p_preprocessor_line(p): - """ asm : preproc_line + """ line : preproc_line """ p[0] = None @@ -1437,7 +1459,8 @@ def generate_binary(outputfname, format_, progname=''): import basic # Minimalist basic tokenizer program = basic.Basic() - program.add_line([['CLEAR', org - 1]]) + if org > 16383: # Only for zx48k: CLEAR if below 16383 + program.add_line([['CLEAR', org - 1]]) program.add_line([['LOAD', '""', program.token('CODE')]]) if OPTIONS.autorun.value: diff --git a/ast_/tree.py b/ast_/tree.py index 69cdddd8e..9f5f5f3b6 100644 --- a/ast_/tree.py +++ b/ast_/tree.py @@ -10,9 +10,9 @@ class NotAnAstError(Error): - ''' Thrown when the "pointer" is not + """ 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) @@ -22,8 +22,8 @@ def __str__(self): class Tree(object): - ''' Simple tree implementation - ''' + """ Simple tree implementation + """ class childrenList(object): def __init__(self, node): assert isinstance(node, Tree) @@ -99,9 +99,9 @@ def children(self, value): self.children.append(x) def inorder(self, funct, stopOn=None): - ''' Iterates in order, calling the function with the current node. + """ Iterates in order, calling the function with the current node. If stopOn is set to True or False, it will stop on true or false. - ''' + """ if stopOn is None: for i in self.children: i.inorder(funct) @@ -113,9 +113,9 @@ def inorder(self, funct, stopOn=None): return funct(self) def preorder(self, funct, stopOn=None): - ''' Iterates in preorder, calling the function with the current node. + """ Iterates in preorder, calling the function with the current node. If stopOn is set to True or False, it will stop on true or false. - ''' + """ if funct(self.symbol) == stopOn and stopOn is not None: return stopOn @@ -128,9 +128,9 @@ def preorder(self, funct, stopOn=None): return stopOn def postorder(self, funct, stopOn=None): - ''' Iterates in postorder, calling the function with the current node. + """ Iterates in postorder, calling the function with the current node. If stopOn is set to True or False, it will stop on true or false. - ''' + """ if stopOn is None: for i in range(len(self.children) - 1, -1, -1): self.children[i].postorder(funct) @@ -141,20 +141,20 @@ def postorder(self, funct, stopOn=None): return funct(self.symbol) def appendChild(self, node): - ''' Appends the given node to the current children list - ''' + """ Appends the given node to the current children list + """ self.children.append(node) def prependChild(self, node): - ''' Inserts the given node at the beginning of the children list - ''' + """ Inserts the given node at the beginning of the children list + """ self.children.insert(0, node) @classmethod def makenode(clss, symbol, *nexts): - ''' Stores the symbol in an AST instance, + """ Stores the symbol in an AST instance, and left and right to the given ones - ''' + """ result = clss(symbol) for i in nexts: if i is None: diff --git a/basic.py b/basic.py index 97f68ba98..2b801c5d5 100755 --- a/basic.py +++ b/basic.py @@ -29,7 +29,7 @@ 'CLS': 251, 'CLEAR': 253, 'PAUSE': 242, - 'LET': 231, + 'LET': 241, 'INPUT': 238, 'READ': 227, 'DATA': 228, diff --git a/examples/eval.bas b/examples/eval.bas new file mode 100644 index 000000000..3929e3b76 --- /dev/null +++ b/examples/eval.bas @@ -0,0 +1,17 @@ + +#include + +' Some Sinclair BASIC tokens ASCII codes +#define FOR_ 235 +#define TO_ 204 +#define BORDER_ 231 +#define PAUSE_ 242 +#define NEXT_ 243 + +REM Executes "FOR i = 0 TO 7: BORDER i: PAUSE 40: NEXT i" twice +FOR i = 1 TO 2 +EvalBasic(CHR$(FOR_, CODE "i", CODE "=", 48, TO_, 55, CODE ":", _ + BORDER_, CODE "i", CODE ":", PAUSE_, 52, 48, CODE ":", _ + NEXT_, CODE "i", 13)) +NEXT + diff --git a/examples/inputexample.bas b/examples/inputexample.bas new file mode 100644 index 000000000..977098bcf --- /dev/null +++ b/examples/inputexample.bas @@ -0,0 +1,7 @@ +#include + +print at 10, 5; "Type something: "; +a$ = input(20) +print +print "You typed: "; a$ + diff --git a/examples/pong.bas b/examples/pong.bas index ec4270e4b..bf9e5d09b 100644 --- a/examples/pong.bas +++ b/examples/pong.bas @@ -24,6 +24,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 @@ -40,10 +42,11 @@ 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 LET xx = 31: FOR yy = minY TO maxY STEP 2: GOSUB 2000: NEXT yy +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 @@ -58,7 +61,8 @@ 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 +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 @@ -80,7 +84,8 @@ delay = dSlow END IF END IF - ELSEIF x > maxX THEN LET x = maxX: LET dx = -dx: BEEP 0.02,20 + 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 @@ -105,13 +110,15 @@ 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 +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 +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 @@ -121,10 +128,10 @@ 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" THEN: REM Must go up +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" THEN + 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 @@ -186,7 +193,7 @@ 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 +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 @@ -196,7 +203,8 @@ 3050 LET NUM = sc Mod 10 3060 GOSUB 5000 3070 NEXT player -3080 RETURN +3080 GOSUB 6000 +3090 RETURN 5000 REM Prints Number NUM at atY, atX 5010 FOR ny = 0 TO 2: FOR nx = 0 TO 1 @@ -204,3 +212,23 @@ 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 + + diff --git a/examples/randplot.bas b/examples/randplot.bas new file mode 100644 index 000000000..c383efbce --- /dev/null +++ b/examples/randplot.bas @@ -0,0 +1,13 @@ + 1 REM ON .. GOTO usage example + 5 DIM x, y as Integer + 7 DIM s as UInteger +10 CLS: LET x=188: LET y=87: LET s=1: RANDOMIZE 1 +20 LET s=s+1: IF x>0 AND x<255 AND y>0 AND y<175 THEN PLOT x,y: PRINT AT 22,0;s:END IF +22 IF s=65535 THEN STOP:END IF +25 ON RND * 4 GOTO 30, 40, 50, 60 + +30 LET x=x+1: GO TO 20 +40 LET x=x-1: GO TO 20 +50 LET y=y+1: GO TO 20 +60 LET y=y-1: GO TO 20 + diff --git a/examples/zxuno/mandel.bas b/examples/zxuno/mandel.bas new file mode 100644 index 000000000..00433554a --- /dev/null +++ b/examples/zxuno/mandel.bas @@ -0,0 +1,172 @@ +'Compile with Boriel ZX Basic Compiler +'Parameters: -t -B -a -O3 + +'(C)2015 Miguel Angel Rodriguez Jodar (mcleod_ideafix) +'miguel.angel@zxprojects.com + +'Distributed under GPL license + +#include + +#define XMAX 127 +#define YMAX 95 +#define PART 32 'Initial dimension of a square +#define ESC 15 'Max. iterations for escape time algorithm (hint: we use colours 1-15) + + +declare sub PutPal (entry as ubyte, col as ubyte) +declare function GetPal (entry as ubyte) as ubyte +declare function MakeRGB (r as ubyte, g as ubyte, b as ubyte) as ubyte + +'------------------------------------------------------------------------------ + +Dim i as ubyte +border 0 +cls 'A pixel with value 0 means pixel not processed. Processed pixels have values 1-15 + +RadastanMode(1) + +for i = 0 to 7 'Initialize all 64 entries of the ULAplus palette + PutPal (i, MakeRGB(i,0,0)) 'negro -> rojo + PutPal (i+8, MakeRGB(7,i,0)) 'rojo -> amarillo + PutPal (i+16, MakeRGB(7,7,i/2)) 'amarillo -> blanco + PutPal (i+24, MakeRGB(7,7-i,3)) 'blanco -> magenta + PutPal (i+32, MakeRGB(7-i,0,3)) 'magenta -> azul + PutPal (i+40, MakeRGB(0,i,3)) 'azul -> cyan + PutPal (i+48, MakeRGB(0,7,(7-i)/2)) 'cyan -> verde + PutPal (i+56, MakeRGB(0,7-i,0)) 'verde -> negro +next i + +'Main loop: divide screen into PARTxPART squares and process each one +dim y as ubyte +dim x as uinteger +for y = 0 to YMAX step PART + for x = 0 to XMAX step PART + mandel (x,y,PART) 'First call to recursive function + next x +next y + +'Palette cycling animation +Dim color0 as ubyte +BucleCicloPaleta: +pause 4 +color0 = GetPal(0) +for i = 0 to 62 + PutPal (i, GetPal((i+1) mod 64)) +next i +PutPal (63,color0) + +if inkey$="" then + goto BucleCicloPaleta +end if + +RadastanMode(0) +border 7 +cls +End + +'------------------------------------------------------------------------------ + +function escape (x as uinteger, y as ubyte) as ubyte + dim cr,ci,zr,zi,tr,ti as fixed + dim c as uinteger + dim zrc,zic as fixed + + cr = -2.4+x*3.2/XMAX + ci = -1.2+y*2.4/YMAX + zr = cr + zi = ci + zrc = cr*cr + zic = ci*ci + c = 1 + while (zrc+zic)<4 and c<>ESC + tr = zrc-zic+cr + ti = 2*zr*zi+ci + zr = tr + zi = ti + zrc = zr*zr + zic = zi*zi + c = c + 1 + end while + return c +end function + + +#define PlotR RadastanPlot +#define PixelR RadastanPoint +#define DrawRH(x, y, l, c) RadastanHLine(x, y, x + l - 1, c) + +sub mandel (x as uinteger, y as ubyte, lv as ubyte) + dim co1,co2 as ubyte + dim xx as uinteger + dim yy as ubyte + + 'Base case: compute colour for all pixels of the 2x2 square + if lv = 2 then + co1 = escape (x,y) + RadastanPlot (x,y,co1) + co1 = escape (x+1,y) + RadastanPlot (x+1,y,co1) + co1 = escape (x,y+1) + RadastanPlot (x,y+1,co1) + co1 = escape (x+1,y+1) + RadastanPlot (x+1,y+1,co1) + return + end if + + 'Compute colours for the perimeter of the current square + co1 = RadastanPoint (x,y) 'Read first pixel. If not processed... + if co1 = 0 then '...compute and plot it. + co1 = escape (x,y) + RadastanPlot (x,y,co1) + end if + for yy = y to y+lv-1 + xx = x + do + co2 = PixelR (xx,yy) 'Read current pixel. If not processed... + if co2 = 0 then + co2 = escape (xx,yy) '... compute and plot it + PlotR (xx,yy,co2) + end if + if co1<>co2 then 'Are they different? + goto subdividir 'then, abort perimeter calculation and go directly to subdivision + end if + if yy=y or yy=y+lv-1 then + xx = xx + 1 + else + xx = xx + lv-1 + end if + loop until xx>=x+lv + next yy + + 'If we reach here, all pixels of the perimeter have the same escape time, so... + for yy=y to y+lv-1 '... fill the square with the same colour + DrawRH(x,yy,lv,co1) + next yy + return + + 'If we reach here, we must partition the current square and recursively start over again +subdividir: + mandel (x,y,lv/2) + mandel (x+lv/2,y,lv/2) + mandel (x,y+lv/2,lv/2) + mandel (x+lv/2,y+lv/2,lv/2) +end sub + +'------------------------------------------------------------------------------ + +sub PutPal (entry as ubyte, col as ubyte) + out 48955,entry + out 65339,col +end sub + +function GetPal (entry as ubyte) as ubyte + out 48955,entry + return in 65339 +end function + +function MakeRGB (r as ubyte, g as ubyte, b as ubyte) as ubyte + return ((g band 7) shl 5) bor ((r band 7) shl 2) bor (b band 3) +end function + + diff --git a/keywords.py b/keywords.py index b7c6032f9..02fcc8d53 100755 --- a/keywords.py +++ b/keywords.py @@ -50,6 +50,7 @@ 'else': 'ELSE', 'elseif': 'ELSEIF', 'end': 'END', + 'endif': 'ENDIF', 'error': 'ERROR', 'exit': 'EXIT', 'exp': 'EXP', @@ -77,6 +78,7 @@ 'mod': 'MOD', 'next': 'NEXT', 'not': 'NOT', + 'on': 'ON', 'or': 'OR', 'out': 'OUT', 'over': 'OVER', @@ -88,8 +90,10 @@ 'poke': 'POKE', 'print': 'PRINT', 'randomize': 'RANDOMIZE', - 'rnd': 'RND', + 'read': 'READ', + 'restore': 'RESTORE', 'return': 'RETURN', + 'rnd': 'RND', 'save': 'SAVE', 'sgn': 'SGN', 'shl': 'SHL', diff --git a/library-asm/array.asm b/library-asm/array.asm index c5c4ff696..b2d185e6f 100644 --- a/library-asm/array.asm +++ b/library-asm/array.asm @@ -42,11 +42,10 @@ __ARRAY: ld hl, 0 ; BC = Offset "accumulator" +LOOP: #ifdef __CHECK_ARRAY_BOUNDARY__ pop de #endif - -LOOP: pop bc ; Get next index (Ai) from the stack #ifdef __CHECK_ARRAY_BOUNDARY__ @@ -74,15 +73,8 @@ LOOP: exx pop de ; DE = Max bound Number (i-th dimension) -#ifdef __CHECK_ARRAY_BOUNDARY__ - push de -#endif ;call __MUL16_FAST ; HL *= DE call __FNMUL -#ifdef __CHECK_ARRAY_BOUNDARY__ - pop de - dec de -#endif jp LOOP ARRAY_END: diff --git a/library-asm/break.asm b/library-asm/break.asm index 6ded698f2..ae8570858 100644 --- a/library-asm/break.asm +++ b/library-asm/break.asm @@ -8,14 +8,21 @@ CHECK_BREAK: PROC - LOCAL PPC, TS_BRK + LOCAL PPC, TS_BRK, NO_BREAK + push af call TS_BRK - ret c + jr c, NO_BREAK - ld (PPC), HL + ld (PPC), hl ; line num ld a, ERROR_BreakIntoProgram - jp __ERROR + 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 diff --git a/library-asm/error.asm b/library-asm/error.asm index 153ecabb8..952cac736 100644 --- a/library-asm/error.asm +++ b/library-asm/error.asm @@ -18,6 +18,7 @@ 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 diff --git a/library-asm/ftou32reg.asm b/library-asm/ftou32reg.asm index 394bda4fb..d2e3e9e04 100644 --- a/library-asm/ftou32reg.asm +++ b/library-asm/ftou32reg.asm @@ -6,6 +6,7 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS PROC LOCAL __IS_FLOAT + LOCAL __NEGATE or a jr nz, __IS_FLOAT @@ -62,10 +63,26 @@ __FTOU32REG_LOOP: __FTOU32REG_END: pop af ; Take the sign bit or a ; Sets SGN bit to 1 if negative - jp m, __NEG32 ; Negates DEHL + 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 diff --git a/library-asm/memcopy.asm b/library-asm/memcopy.asm index 24b1f45de..d098cddc4 100644 --- a/library-asm/memcopy.asm +++ b/library-asm/memcopy.asm @@ -8,7 +8,7 @@ ; ---------------------------------------------------------------- ; Emulates both memmove and memcpy C routines -; Blocks will safely copies if they overlap +; Block will be safely copied if they overlap ; HL => Start of source block ; DE => Start of destiny block @@ -19,16 +19,16 @@ __MEMCPY: PROC LOCAL __MEMCPY2 - push hl - add hl, bc + 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 set => DE > HL + pop hl ; recovers HL. If carry => DE > HL + BC (no overlap) jr c, __MEMCPY2 ; Now checks if DE <= HL - sbc hl, de + sbc hl, de ; Even if overlap, if DE < HL then we can LDIR safely add hl, de jr nc, __MEMCPY2 @@ -45,5 +45,5 @@ __MEMCPY: __MEMCPY2: ldir ret - + ENDP diff --git a/library-asm/ongoto.asm b/library-asm/ongoto.asm new file mode 100644 index 000000000..9b6759811 --- /dev/null +++ b/library-asm/ongoto.asm @@ -0,0 +1,31 @@ +; ------------------------------------------------------ +; Implements ON .. GOTO +; ------------------------------------------------------ + +__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) diff --git a/library-asm/random.asm b/library-asm/random.asm index 958d5f9a3..03d01e1de 100644 --- a/library-asm/random.asm +++ b/library-asm/random.asm @@ -1,7 +1,5 @@ ; RANDOM functions -#include once - RANDOMIZE: ; Randomize with 32 bit seed in DE HL ; if SEED = 0, calls ROM to take frames as seed diff --git a/library-asm/read_restore.asm b/library-asm/read_restore.asm new file mode 100644 index 000000000..a31de87c0 --- /dev/null +++ b/library-asm/read_restore.asm @@ -0,0 +1,350 @@ +;; 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 +__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__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 + 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 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__0 + ENDP + +#undef _str +#undef _i8 +#undef _u8 +#undef _i16 +#undef _u16 +#undef _i32 +#undef _u32 +#undef _f16 +#undef _flt diff --git a/library-asm/swap32.asm b/library-asm/swap32.asm index 56acde3f9..805bcf57e 100644 --- a/library-asm/swap32.asm +++ b/library-asm/swap32.asm @@ -4,13 +4,13 @@ __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret diff --git a/library/basic.bas b/library/basic.bas new file mode 100644 index 000000000..a777b314b --- /dev/null +++ b/library/basic.bas @@ -0,0 +1,146 @@ + +/' -------------------------------------------------------------------------------------- +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 + 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 + END ASM +End Function + +#pragma pop(case_insensitive) +#require "error.asm" + +#endif \ No newline at end of file diff --git a/library/esxdos.bas b/library/esxdos.bas index ac792a751..609d0c393 100644 --- a/library/esxdos.bas +++ b/library/esxdos.bas @@ -135,6 +135,8 @@ Function FASTCALL ESXDosWrite(ByVal handle as Byte, _ 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 @@ -169,10 +171,10 @@ End Function Function FASTCALL ESXDosRead(ByVal handle as Byte, _ ByVal buffer as UInteger, _ ByVal nbytes as UInteger) as Uinteger - poke EDOS_ERR_NR,255 - 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 diff --git a/library/haplofnt.bin b/library/haplofnt.bin new file mode 100644 index 000000000..af728a861 Binary files /dev/null and b/library/haplofnt.bin differ diff --git a/library/input.bas b/library/input.bas index 0e50f990e..abb2ae04c 100644 --- a/library/input.bas +++ b/library/input.bas @@ -29,6 +29,7 @@ FUNCTION input(MaxLen AS UINTEGER) AS STRING DIM i as UINTEGER result$ = "" + POKE 23611, PEEK 23611 bOR 8 DO PRIVATEInputShowCursor() @@ -39,21 +40,18 @@ FUNCTION input(MaxLen AS UINTEGER) AS STRING PRIVATEInputHideCursor() - IF LastK = 12 THEN: - IF LEN(result$) THEN : REM "Del" key code is 12 - IF LEN(result$) = 1 THEN: + 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 - - ELSE IF LastK >= CODE(" ") AND LEN(result$) < MaxLen THEN + ELSEIF LastK >= CODE(" ") AND LEN(result$) < MaxLen THEN LET result$ = result$ + CHR$(LastK) PRINT CHR$(LastK); - END IF - END IF LOOP UNTIL LastK = 13 : REM "Enter" key code is 13 diff --git a/library/input42.bas b/library/input42.bas new file mode 100644 index 000000000..ba0fb340f --- /dev/null +++ b/library/input42.bas @@ -0,0 +1,91 @@ +' ---------------------------------------------------------------- +' 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) +' ---------------------------------------------------------------- +#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 + diff --git a/library/memcopy.bas b/library/memcopy.bas index 8abbf393e..6e72c152b 100644 --- a/library/memcopy.bas +++ b/library/memcopy.bas @@ -1,10 +1,10 @@ ' ---------------------------------------------------------------- ' 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 +' Copyleft (k) 2008-2018 +' Contributed by: +' - Jose Rodriguez-Rosa (a.k.a. Boriel) +' - Miguel Angel Diaz-Jodar (a.k.a. McLeod_Ideafix) ' ---------------------------------------------------------------- #ifndef __LIBRARY_MEMCOPY__ @@ -13,20 +13,21 @@ REM Avoid recursive / multiple inclusion #define __LIBRARY_MEMCOPY__ -#define memmove memcopy - +#pragma push(case_insensitive) +#pragma case_insensitive = True ' ---------------------------------------------------------------- -' sub MEMCOPY(sourceaddr, destaddr, blocklength) +' 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 from source to dest. Block may overlap +' Copies block of memory safely from source to dest. +' Source and destiny blocks may overlap ' ---------------------------------------------------------------- -sub fastcall memcopy(source as uinteger, dest as uinteger, length as uinteger) +sub fastcall MemMove(source as uinteger, dest as uinteger, length as uinteger) asm ; Emulates both memmove and memcpy C routines ; Blocks will safely copies if they overlap @@ -36,21 +37,88 @@ sub fastcall memcopy(source as uinteger, dest as uinteger, length as uinteger) ; BC => Block length exx - pop hl ; ret addr - 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 + end asm +end sub + - pop de ; dest - pop bc ; length +' ---------------------------------------------------------------- +' 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 slighly faster than memmove +' ---------------------------------------------------------------- +sub fastcall MemCopy(source as uinteger, dest as uinteger, length as uinteger) + asm +; 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 - - jp __MEMCPY + ldir 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 + +; 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 + end asm +end sub + + #require "memcopy.asm" +#pragma pop(case_insensitive) + #endif diff --git a/library/print42.bas b/library/print42.bas index 58eea735d..bbe8ba44c 100644 --- a/library/print42.bas +++ b/library/print42.bas @@ -24,7 +24,7 @@ asm LD A, H OR L - RET Z + JP Z, print42end LD C,(HL) INC HL @@ -32,7 +32,7 @@ asm LD A, C OR B - JP Z, print64end ; Is the length of the string 0? If so, quit. + 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. @@ -48,14 +48,13 @@ examineChar: 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. + ;;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. + ;;SBC HL,BC ; Subtract length of string from HL. LD HL, -2 ADD HL, BC EX DE,HL ; Get HL back from DE - ;;RET NC ; If the result WASN'T negative, return. (We need AT to have parameters to make sense) - JP NC, print64end ; If the result WASN'T negative, return. (We need AT to have parameters to make sense) + 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 @@ -63,32 +62,50 @@ isAt: 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 - CALL nxtchar ; Call routine to shuffle right a char - JR newline ; Hop over to + ld (xycoords), de + JR nextChar LOCAL isNewline isNewline: CP 13 ; Is this character a newline? - JR NZ,checkvalid ; If not, jump forward + JR NZ, checkdel ; If not, jump forward LOCAL newline newline: - ;LD DE,(63536) + ld de, (xycoords) CALL nxtline ; move to next line + ld (xycoords), de + JR nextChar - ;LD (63536),DE ; and go on to next character +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 + 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 + CALL printachar ; Go print a character POP BC ; Recover our count POP HL ; Recover our position @@ -97,14 +114,14 @@ 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?) + 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 print64end ; End the print routine + 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. +; 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 @@ -120,8 +137,8 @@ printachar: 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 + 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 @@ -129,16 +146,16 @@ printachar: 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. + 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 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. + ld de, workspace ; Point DE at our 8 byte workspace. push de ; Save it exx ; ld c, a ; Put our kill column in C' @@ -150,21 +167,21 @@ calcChar: ; this is the calculate from the ROM data option LOCAL loop1 loop1: ld a, (hl) ; Load a byte of character data - inc hl ; point at the next byte + 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 + 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 + 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 + inc de ; next workspace byte djnz loop1 ; go round for our other bytes - pop bc ; Recover a pointer to our workspace. + pop bc ; Recover a pointer to our workspace. LOCAL printdata printdata: @@ -259,18 +276,18 @@ hop3: and d or b ld (hl), a ; Write out our byte - inc hl ; Go one byte right + inc hl ; Go one byte right ld a, (hl) ; Bring it in and e - or c ; mix those leftover bits into the next block + or c ; mix those leftover bits into the next block ld (hl), a ; Write it out again pop hl - inc h ; Next line + inc h ; Next line exx - inc bc ; Next workspace byte + inc bc ; Next workspace byte pop af dec a - jr nz, hop4 ; And go back! + jr nz, hop4 ; And go back! exx ; Tidy up pop hl ; Clear stack leftovers @@ -292,20 +309,20 @@ testcoords: LOCAL nxtchar nxtchar: - ld a, e ; - cp 42 ; Are we >42? - jr c, ycoord ; if not, hop forward + 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 + 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 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 @@ -313,8 +330,8 @@ printAt42Coords: asm LOCAL xycoords xycoords: - defb 0 ; x coordinate - defb 0 ; y coordinate + defb 0 ; x coordinate + defb 0 ; y coordinate LOCAL workspace workspace: @@ -334,29 +351,29 @@ workspace: 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 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 224 ; 5 + defb 224 ; 6 defb 192 ; 7 defb 240 ; 8 defb 240 ; 9 @@ -395,40 +412,40 @@ whichcolumn: defb 252 ; Z defb 224 ; [ defb 252 ; \ - defb 240 ; ] - defb 252 ; ^ - defb 240 ; _ - 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 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 @@ -486,10 +503,19 @@ characters: defb 164 defb 180 defb 72 - defb 48 + defb 48 + + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0xFC -LOCAL print64end -print64end: +LOCAL print42end +print42end: ENDP end asm diff --git a/library/radastan.bas b/library/radastan.bas index 4863e179f..67cbe228a 100644 --- a/library/radastan.bas +++ b/library/radastan.bas @@ -1,7 +1,7 @@ ' ---------------------------------------------------------------- -' This file is released under the GPL v3 License +' This file is released under the MIT License ' -' Copyleft (k) 2008 +' Copyleft (k) 2017 ' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ' Radastan mode library for ZX UNO and compatible machines @@ -13,15 +13,22 @@ REM Avoid recursive / multiple inclusion #define __LIBRARY_RADASTAN__ + +#define RADASTAN_FONT RadastanHaploFont + +DIM RadastanScrAddr as UInteger = 16384 +DIM RadastanColRow as UInteger = 0 +DIM RadastanFontAddr as UInteger = @RADASTAN_FONT + + +REM Dummy lines (leave them for now) +dummy = RadastanScrAddr + RadastanColRow + RadastanFontAddr + ' ---------------------------------------------------------------- -' function NAME +' function RadastanMode ' ' Parameters: -' x -' y -' -' Returns: -' (When nothing to return, use SUB instead) +' enable: 0 => disable (normal mode), otherwise radastan mode ' ---------------------------------------------------------------- sub RadastanMode(enable as Ubyte) OUT 64571, 64 @@ -30,7 +37,21 @@ end sub ' ---------------------------------------------------------------- -' function RadastanPlot +' function MakeRGB +' Converts an R, G, B color to a byte +' +' Parameters: +' r: Red component +' g: Green component +' b: Blue component +' ---------------------------------------------------------------- +function MakeRGB (r as ubyte, g as ubyte, b as ubyte) as ubyte + return ((g band 7) shl 5) bor ((r band 7) shl 2) bor (b band 3) +end function + + +' ---------------------------------------------------------------- +' Sub RadastanPlot ' ' Parameters: ' x: coord x (horizontal) of pixel to plot @@ -59,43 +80,138 @@ COORDS EQU 5C7Dh adc a, a xor 1 ld h, a + ld c, 0 ld a, d ;' recuperamos el valor vertical - rrca - rrca ;' rotamos para dejar su valor en multiplos de 64 (linea, de dos en dos pixels) - and 192 ;' borramos el resto de bits por si las moscas - or e ;' sumamos el valor horizontal - ld e, a ;' e preparado - ld a, d ;' cargamos el valor vertical - rrca - rrca ;' rotamos para quedarnos con los bits altos - and 63 ;' borramos el resto de bits - or 64 ;' nos posicionamos a partir de 16384 (16384=64+0 en dos bytes) - ld d, a ;' d preparado, ya tenemos la posicion en pantalla - ld a,(de) - rr h + rra + rr c + rra + rr c + ld b, a + ld a, e + add a, c + ld c, a + ex de, hl + ld hl, (_RadastanScrAddr) + add hl, bc + ld a,(hl) + rr d jr c, rplotnext2 and 240 - or l + or e jr rplotfin rplotnext2: and 15 - rl l - rl l - rl l - rl l - or l + rl e + rl e + rl e + rl e + or e rplotfin: - ld (de), a + ld (hl), a ENDP end asm end sub -sub RadastanDraw(ByVal x1 as Byte, ByVal y1 as Byte, Byval colorIdx as Ubyte) - DIM dx, dy, dx2, dy2, x, y, x0, y0, sx, sy as Byte - DIM p, iE, iNE as Byte +' ---------------------------------------------------------------- +' Sub RadastanPoint +' +' Parameters: +' x: coord x (horizontal) of pixel to examine +' y: coord y (vertical) of pixel to examine +' +' Returns: +' color: color palette (0..15) or -1 if out of screen +' ---------------------------------------------------------------- +Function fastcall RadastanPoint(ByVal x as ubyte, ByVal y as ubyte) as Byte + ASM + PROC + LOCAL next2 + pop hl ; ret addr + ex (sp), hl ; callee => h = y + ld e, a ; E = x + ld a, 127 + cp e + ld a, -1 + ret c ; Out of screen + ld a, 95 + cp h + ld a, -1 + ret c ; Out of screen + xor a + rr e + adc a, a + ld c, a ; c = 0 if even, 1 if odd + ld a, h + rrca + rrca + and 192 + or e + ld l, a + ld a, h + rrca + rrca + and 63 + ld h, a + ld de, (_RadastanScrAddr) + add hl, de + ld a, (hl) + rr c + jr c, next2 + rra + rra + rra + rra +next2: + and 0xF + ENDP + END ASM +End Function + + +' ---------------------------------------------------------------- +' Sub RadastanHLine +' +' Draws an horizontal line from (x0, y) to (x1, y) +' +' Parameters: +' x0: coord x (horizontal) of first pixel to plot +' y: coord y (vertical) of first pixel to plot +' x1: coord x (horizontal) of the last pixel to plot +' color: color palette (0..15) +' ---------------------------------------------------------------- +Sub RadastanHLine(ByVal x0 as UByte, ByVal y as UByte, ByVal x1 as UByte, ByVal col as UByte) + Dim x as UByte + + if x1 < x0 + x = x1 + x1 = x0 + x0 = x + end if + + FOR x = x0 TO x1 + RadastanPlot(x, y, col) + NEXT x + +End Sub + + +' ---------------------------------------------------------------- +' Sub RadastanDraw +' +' Draws a line from the last plotted position to the given coords. +' +' Parameters: +' x: coord x (horizontal) of last pixel to plot +' y: coord y (vertical) of last pixel to plot +' color: color palette (0..15) +' ---------------------------------------------------------------- +SUB RadastanDraw(ByVal x1 as Byte, ByVal y1 as Byte, Byval colorIdx as Ubyte) + DIM sx, sy as Byte + DIM x, y, x0, y0 as Byte + DIM p, dx, dy, iE, iNE as Integer LET x0 = PEEK 5C7Dh LET y0 = PEEK 5C7Eh @@ -146,17 +262,119 @@ sub RadastanDraw(ByVal x1 as Byte, ByVal y1 as Byte, Byval colorIdx as Ubyte) RadastanPlot(x, y, colorIdx) END WHILE END IF -end sub +END SUB + + +' ---------------------------------------------------------------- +' Sub RadastanCircle +' +' Draws a Circle of radius r with center (x, y) +' +' Parameters: +' x: coord x (horizontal) of circle center +' y: coord y (vertical) of circle center +' r: radius (in pixels) +' color: color palette (0..15) +' ---------------------------------------------------------------- +SUB RadastanCircle(ByVal x0 as Byte, ByVal y0 as Byte, ByVal r as Byte, ByVal colorIdx as UByte) + DIM x, y, dx, dy, err as Byte + + x = r - 1 + y = 0 + dx = 1 + dy = 1 + err = dx - (r << 1) + + WHILE x >= y + RadastanPlot(x0 + x, y0 + y, colorIdx) + RadastanPlot(x0 + y, y0 + x, colorIdx) + RadastanPlot(x0 - y, y0 + x, colorIdx) + RadastanPlot(x0 - x, y0 + y, colorIdx) + RadastanPlot(x0 - x, y0 - y, colorIdx) + RadastanPlot(x0 - y, y0 - x, colorIdx) + RadastanPlot(x0 + y, y0 - x, colorIdx) + RadastanPlot(x0 + x, y0 - y, colorIdx) + + IF err <= 0 THEN + y = y + 1 + err = err + dy + dy = dy + 2 + END IF + + IF err > 0 THEN + x = x - 1 + dx = dx + 2 + err = err + (-r << 1) + dx + END IF + END WHILE +END SUB + +' ---------------------------------------------------------------- +' Sub RadastanFillCircle +' +' Fills a Circle of radius r with center (x, y) +' +' Parameters: +' x: coord x (horizontal) of circle center +' y: coord y (vertical) of circle center +' r: radius (in pixels) +' color: color palette (0..15) +' ---------------------------------------------------------------- +SUB RadastanFillCircle(ByVal x0 as Byte, ByVal y0 as Byte, ByVal r as Byte, ByVal colorIdx as UByte) + DIM x, y, dx, dy, err as Byte -sub RadastanPalette(ByVal colorIndex as Ubyte, ByVal rgb as UByte) ' color=0-15, rgb = binary GGGRRRBB - OUT 48955, 64: OUT 65339, 1 + x = r - 1 + y = 0 + dx = 1 + dy = 1 + err = dx - (r << 1) + + WHILE x >= y + RadastanHLine(x0 - x, y0 + y, x0 + x, colorIdx) + RadastanHLine(x0 - y, y0 + x, x0 + y, colorIdx) + RadastanHLine(x0 - x, y0 - y, x0 + x, colorIdx) + RadastanHLine(x0 - y, y0 - x, x0 + y, colorIdx) + + IF err <= 0 THEN + y = y + 1 + err = err + dy + dy = dy + 2 + END IF + + IF err > 0 THEN + x = x - 1 + dx = dx + 2 + err = err + (-r << 1) + dx + END IF + END WHILE +END SUB + + +' ---------------------------------------------------------------- +' sub RadastanPalette +' +' Defines a palette entry +' +' Parameters: +' colorIndex: Palete index entry (0..15) +' rgb: Color value rgb = binary GGGRRRBB +' ---------------------------------------------------------------- +SUB RadastanPalette(ByVal colorIndex as Ubyte, ByVal rgb as UByte) ' OUT 48955, colorIndex: OUT 65339, rgb -end sub +END SUB -sub fastcall RadastanCls(color as ubyte) - asm +' ---------------------------------------------------------------- +' Sub RadastanCls +' +' Clears the screen with the given color +' +' Parameters: +' col: Color index (0..15) +' ---------------------------------------------------------------- +SUB fastcall RadastanCls(ByVal col as UByte) + ASM and 0xF ld b, a rla @@ -164,13 +382,371 @@ sub fastcall RadastanCls(color as ubyte) rla rla or b - ld hl, 16384 - ld de, 16385 + ld hl, (_RadastanScrAddr) + ld d, h + ld e, l + inc de ld bc, 6143 ld (hl), a ldir - end asm -end Sub + ld hl, 0 + ld (5C7Dh), hl ; COORDS + ld (_RadastanColRow), hl + END ASM +END SUB + + +' ---------------------------------------------------------------- +' sub RadastanFill +' +' Fills the figure with the given color starting from the given +' coordinate. +' +' Parameters: +' x: coord x (horizontal) of starting point +' y: coord y (vertical) of starting point +' color: fill color, palette (0..15) +' ---------------------------------------------------------------- +SUB RadastanFill(Byval x as UByte, ByVal y as UByte, ByVal col as Ubyte) + Const L as Uinteger = 1023 + Const L2 as Uinteger = L * 2 + 1 + DIM buff(L, 1) as UByte + DIM i, j, paddr as Uinteger + DIM c as Byte + + paddr = @buff(0, 0) + +#define P(x, y) \ + POKE paddr + j, x \ + j = j + 1 \ + POKE paddr + j, y \ + j = (j + 1) bAND L2 + + c = RadastanPoint(x, y) + IF c = -1 THEN ' -1 => Out of Screen + RETURN + END IF + + i = 0 + j = 0 + P(x, y) + + WHILE i <> j + x = PEEK(paddr + i) + i = i + 1 + y = PEEK(paddr + i) + i = (i + 1) bAND L2 + + IF c <> RadastanPoint(x, y) THEN + CONTINUE WHILE + END IF + + RadastanPlot(x, y, col) + P(x + 1, y) + P(x - 1, y) + P(x, y + 1) + P(x, y - 1) + END WHILE + +#undef P +END SUB + + +' ---------------------------------------------------------------- +' Sub RadastanPrintAt +' +' Places the printing cursor at the given row, column coordinates +' +' Parameters: +' row: cursor row (0..31) +' col: cursor column (0..15) +' ---------------------------------------------------------------- +SUB FASTCALL RadastanPrintAt(ByVal row as UByte, ByVal col as Ubyte) + ASM + ; A register contains row + pop hl + ex (sp), hl ; h = col + ld l, h + ld h, a + ld (_RadastanColRow), hl + ret + END ASM +END SUB + + +' ---------------------------------------------------------------- +' sub RadastanSetFont +' +' Sets the font to be used for printing by passing the address to +' it. +' +' Parameters: +' fontaddress: memory address of the font memory area +' ---------------------------------------------------------------- +SUB FASTCALL RadastanSetFont(ByVal fontaddress as Uinteger) + RadastanFontAddr = fontaddress +END SUB + + +' ---------------------------------------------------------------- +' sub RadastanSetScreenAddr +' +' Sets the start of the screen for these routines +' +' Parameters: +' scraddr: memory address of the beginning of screen area +' ---------------------------------------------------------------- +SUB FASTCALL RadastanSetScreenAddr(ByVal scraddr as Uinteger) + RadastanScrAddr = scraddr +END SUB + + +' ---------------------------------------------------------------- +' Sub RadastanPrintChar +' +' Prints the current character (ASCII Code) at the current +' cursor position. The cursor is updated. +' ---------------------------------------------------------------- +SUB FASTCALL RadastanPrintChar(ByVal char as UByte) + ASM + PROC + LOCAL no_inter + + ; FASTCALL => a reg contains char + push ix + ld ix, 0 + add ix, sp + sub ' ' + ld b, a + add a, a + add a, b ; multiplico por 3 + ld h, 0 + ld l, a + add hl, hl + add hl, hl ; multiplico por 12 + + ld bc, (_RadastanFontAddr) + add hl, bc + ld a, r + ex af, af' + di + ld sp, hl + + ld a, (_RadastanColRow + 1) + ld l, a + add a, a + add a, l + add a, a + ld l, 0 + rra + rr l + rra + rr l + ld h, a ; hl = a * 64 => row * 6 * 64 + ld a, (_RadastanColRow) ; col + add a, a + add a, l + ld l, a + + ld bc, (_RadastanScrAddr) ; Screen offset + add hl, bc + ld bc, 63 + + pop de + ld (hl), e ; pinto primera fila + inc hl + ld (hl), d + add hl, bc + + pop de + ld (hl), e ; pinto segunda fila + inc hl + ld (hl), d + add hl, bc + + pop de + ld (hl), e ; pinto tercera fila + inc hl + ld (hl), d + add hl, bc + + pop de + ld (hl), e ; pinto cuarta fila + inc hl + ld (hl), d + add hl, bc + + pop de + ld (hl), e ; pinto quinta fila + inc hl + ld (hl), d + add hl, bc + + pop de + ld (hl), e ; pinto sexta fila + inc hl + ld (hl), d + + ld sp, ix + ex af, af' + jp po, no_inter + ei +no_inter: + pop ix + + ld a, 31 +__RADASTAN_NEXT_PRN_POS: + ld hl, _RadastanColRow + inc (hl) + cp (hl) + ret nc + xor a + ld (hl), a + inc hl + inc (hl) + ld a, 15 + cp (hl) + ret nc + ld (hl), a + ld a, 6 + jp _RadastanScrollUp + + ENDP + END ASM + DIM dummy as Uinteger + dummy = @RadastanScrollUp +END SUB + + +' ---------------------------------------------------------------- +' SUB RadastanPrint +' +' Prints the given string in radastan mode. Allows chr$(13) as +' newline and chr$(10) as return +' ---------------------------------------------------------------- +SUB RadastanPrint(ByVal s as String) + DIM dummy as Uinteger + dummy = @RadastanPrintChar + ASM + PROC + LOCAL loop, next_char, no_newline, no_line_return, finish + ld l, (ix + 4) + ld h, (ix + 5) + ld c, (hl) + inc hl + ld b, (hl) + ld a, b + or c + jr z, finish + ld a, c + ld c, b + ld b, a + inc c + +loop: + inc hl + ld a, (hl) + cp 13 + jr nz, no_newline + xor a + exx + call __RADASTAN_NEXT_PRN_POS + exx + jp next_char + +no_newline: + cp 10 + jr nz, no_line_return + xor a + ld (_RadastanColRow), a + jp next_char + +no_line_return: + exx + call _RadastanPrintChar + exx + +next_char: + djnz loop + dec c + jp nz, loop + +finish: + ENDP + END ASM +END SUB + + +' ---------------------------------------------------------------- +' Sub RadastanHaploFont +' +' Actually not a Sub (do not call!). This just defines the font +' in a function. Use @RadastanHaploFont +' ---------------------------------------------------------------- +SUB FASTCALL RadastanHaploFont + ASM + incbin "haplofnt.bin" + END ASM +END SUB + + +' ---------------------------------------------------------------- +' sub RadastaPrintNL +' +' Prints a New Line (update cursor position to the beginning of +' the new line. +' ---------------------------------------------------------------- +SUB FASTCALL RadastanPrintNL() + ASM + xor a + jp __RADASTAN_NEXT_PRN_POS: + END ASM +END SUB + + +' ---------------------------------------------------------------- +' sub RadastanScrollUp +' +' Scrolls ups the screen the given number of lines (up to 96). +' The new entering lines are filled with color index 0 +' +' Parameters: +' lines: number of lines to scroll up +' ---------------------------------------------------------------- +SUB FASTCALL RadastanScrollUp(ByVal lines As Byte) + ASM + ; scrolls up + cp 97 + ret nc + ccf + ld e, 0 + rra + rr e + rra + rr e + ld d, a ; de = lines * 64 + ld hl, 6144 + sbc hl, de ; hl = 6144 - lines * 64 + push de + + ld b, h + ld c, l ; bc = 6144 - lines * 64 + ex de, hl ; hl = lines * 64 + ld de, (_RadastanScrAddr) + add hl, de ; hl = scr_addr + lines * 64 + ldir + + ; blank last 6 scanlines + xor a + ld (de), a + ld h, d + ld l, e + inc de + pop bc + dec bc + ldir + END ASM +END SUB #endif diff --git a/prepro/args.py b/prepro/args.py index c18da973c..afc656f52 100644 --- a/prepro/args.py +++ b/prepro/args.py @@ -6,8 +6,8 @@ class Arg(object): - ''' Implements an argument (a list of tokens and macrocalls) - ''' + """ Implements an argument (a list of tokens and macrocalls) + """ def __init__(self, value=None, table=None): self.table = table self.value = [] @@ -43,8 +43,8 @@ def __iter__(self): class ArgList(object): - ''' Implements an arglist - ''' + """ Implements an arglist + """ def __init__(self, table): self.table = table self.value = [] diff --git a/prepro/definestable.py b/prepro/definestable.py index dfb0c0cf8..8d4b16f04 100644 --- a/prepro/definestable.py +++ b/prepro/definestable.py @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- # vim: ts=4:sw=4:et: -''' Class for a Table of Defines. +""" Class for a Table of Defines. Each identifier has a dictionary entry. -''' +""" import sys import re @@ -18,20 +18,20 @@ class DefinesTable(object): - ''' A class which will store + """ A class which will store define labels, and its values. It will also susbtitute the current value of a label for the given value. - ''' + """ def __init__(self): - ''' Initializes table - ''' + """ Initializes table + """ self.table = {} def define(self, id_, lineno, value='', fname=None, args=None): - ''' Defines the value of a macro. + """ Defines the value of a macro. Issues a warning if the macro is already defined. - ''' + """ if fname is None: if CURRENT_FILE: fname = CURRENT_FILE[-1] @@ -45,9 +45,9 @@ def define(self, id_, lineno, value='', fname=None, args=None): self.set(id_, lineno, value, fname, args) def set(self, id_, lineno, value='', fname=None, args=None): - ''' Like the above, but issues no warning on duplicate macro + """ Like the above, but issues no warning on duplicate macro definitions. - ''' + """ if fname is None: if CURRENT_FILE: fname = CURRENT_FILE[-1] @@ -60,21 +60,21 @@ def undef(self, id_): del self.table[id_] def defined(self, id_): - ''' Returns if the given ID + """ Returns if the given ID is defined - ''' + """ return id_.strip() in self.table.keys() def __getitem__(self, key): - ''' Returns the ID instance given it's + """ Returns the ID instance given it's _id. If it does not exist, return the _id itself. - ''' + """ return self.table.get(key.strip(), key) def __setitem__(self, key, value): - ''' Assigns the value to the given table entry - ''' + """ Assigns the value to the given table entry + """ k = key.strip() if not RE_ID.match(k): raise PreprocError('"%s" must be an identifier' % key, None) diff --git a/prepro/exceptions.py b/prepro/exceptions.py index 7112ccc62..84b096db5 100644 --- a/prepro/exceptions.py +++ b/prepro/exceptions.py @@ -3,8 +3,8 @@ class PreprocError(Exception): - ''' Denotes an exception in de preprocessor - ''' + """ Denotes an exception in de preprocessor + """ def __init__(self, msg, lineno): self.message = msg self.lineno = lineno diff --git a/prepro/id_.py b/prepro/id_.py index bd6ee8f1a..289e34cf0 100644 --- a/prepro/id_.py +++ b/prepro/id_.py @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- # vim: ts=4:sw=4:et: -__doc__ = ''' A class for an identifier parsed by the preprocessor. +__doc__ = """ A class for an identifier parsed by the preprocessor. It contains it's name, arguments and macro value. -''' +""" import sys @@ -15,9 +15,9 @@ class ID(object): - ''' This class represents an identifier. It's stores a string + """ This class represents an identifier. It's stores a string (the ID name and value by default). - ''' + """ def __init__(self, id_, args=None, value=None, lineno=None, fname=None): if fname is None: fname = CURRENT_FILE[-1] @@ -42,9 +42,9 @@ def __str__(self): return self.name def __dumptable(self, table): - ''' Dumps table on screen + """ Dumps table on screen for debugging purposes - ''' + """ for x in table.table.keys(): sys.stdout.write("{0}\t<--- {1} {2}".format(x, table[x], type(table[x]))) if isinstance(table[x], ID): diff --git a/prepro/macrocall.py b/prepro/macrocall.py index 3c34a0237..ce1049790 100644 --- a/prepro/macrocall.py +++ b/prepro/macrocall.py @@ -8,29 +8,29 @@ class MacroCall(object): - ''' A call to a macro, stored in an object. + """ A call to a macro, stored in an object. Every time the macro() is called, the macro returns it value. - ''' + """ def __init__(self, lineno, table, id_, args=None): - ''' Initializes the object with the ID table, the ID name and + """ Initializes the object with the ID table, the ID name and optionally, the passed args. - ''' + """ self.table = table self.id_ = id_ self.callargs = args self.lineno = lineno def eval(self, arg): - ''' Evaluates a given argument. The token will be returned by default + """ Evaluates a given argument. The token will be returned by default "as is", except if it's a macrocall. In such case it will be evaluated recursively. - ''' + """ return str(arg()) # Evaluate the arg (could be a macrocall) def __call__(self, symbolTable=None): - ''' Execute the macro call using LAZY evaluation - ''' + """ Execute the macro call using LAZY evaluation + """ __DEBUG__("evaluating '%s'" % self.id_, 2) if symbolTable is None: symbolTable = self.table @@ -87,8 +87,8 @@ def __call__(self, symbolTable=None): return tmp def is_defined(self, symbolTable=None): - ''' True if this macro has been defined - ''' + """ True if this macro has been defined + """ if symbolTable is None: symbolTable = self.table diff --git a/setup.py b/setup.py index fd2509511..d4c827998 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ def get_files(folder): url='https://bitbucket.org/boriel/zxbasic', download_url='http://boriel.com/files/zxb/zxbasic-%s.tar.gz' % version.VERSION, keywords=['compiler', 'zxspectrum', 'BASIC', 'z80'], # arbitrary keywords - data_files=[(os.path.join('bin', x), get_files(x)) for x in file_dirs] + ['README.md', 'LICENSE.txt'], + data_files=[(os.path.join('bin', x), get_files(x)) for x in file_dirs], license='GPL3', entry_points={ 'console_scripts': [ diff --git a/symbols/argument.py b/symbols/argument.py index 502203252..f375c03c3 100644 --- a/symbols/argument.py +++ b/symbols/argument.py @@ -87,7 +87,7 @@ def __eq__(self, other): def typecast(self, type_): """ Test type casting to the argument expression. - On sucess changes the node value to the new typecast, and returns + 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. """ self.value = SymbolTYPECAST.make_node(type_, self.value, self.lineno) diff --git a/symbols/arrayaccess.py b/symbols/arrayaccess.py index 5f0d769bd..d89ffec45 100644 --- a/symbols/arrayaccess.py +++ b/symbols/arrayaccess.py @@ -25,7 +25,7 @@ class SymbolARRAYACCESS(SymbolCALL): - ''' Defines an array access. It's pretty much like a function call + """ Defines an array access. It's pretty much like a function call (e.g. A(1, 2) could be an array access or a function call, depending on context). So we derive this class from SymbolCall @@ -36,9 +36,9 @@ class SymbolARRAYACCESS(SymbolCALL): Parameters: entry will be the symboltable entry. Arglist a SymbolARGLIST instance. - ''' + """ def __init__(self, entry, arglist, lineno): - SymbolCALL.__init__(self, entry, arglist, lineno) + super(SymbolARRAYACCESS, self).__init__(entry, arglist, lineno) @property def entry(self): @@ -71,13 +71,13 @@ def scope(self): @property def offset(self): - ''' If this is a constant access (e.g. A(1)) + """ If this is a constant access (e.g. A(1)) return the offset in bytes from the beginning of the variable in memory. Otherwise, if it's not constant (e.g. A(i)) returns None - ''' + """ offset = 0 # Now we must typecast each argument to a u16 (POINTER) type # i is the dimension ith index, b is the bound @@ -97,17 +97,9 @@ def offset(self): @classmethod def make_node(cls, id_, arglist, lineno): - ''' Creates an array access. A(x1, x2, ..., xn) - ''' - assert isinstance(arglist, SymbolARGLIST) - """ - check = gl.SYMBOL_TABLE.check_class(id_, CLASS.array, lineno) - if not check: - return None - - if not gl.SYMBOL_TABLE.check_is_declared(id_, lineno, 'array'): - return None + """ Creates an array access. A(x1, x2, ..., xn) """ + assert isinstance(arglist, SymbolARGLIST) variable = gl.SYMBOL_TABLE.access_array(id_, lineno) if variable is None: return None diff --git a/symbols/block.py b/symbols/block.py index 0ecdc2149..35fa6d4b6 100644 --- a/symbols/block.py +++ b/symbols/block.py @@ -6,7 +6,7 @@ # 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 +# the GNU General License v3 # ---------------------------------------------------------------------- from api.check import is_null @@ -14,29 +14,26 @@ class SymbolBLOCK(Symbol): - ''' Defines a block of code. - ''' + """ Defines a block of code. + """ def __init__(self, *nodes): - Symbol.__init__(self, *(x for x in nodes if not is_null(x))) + super(SymbolBLOCK, self).__init__(*(x for x in nodes if not is_null(x))) @classmethod def make_node(cls, *args): - ''' Creates a chain of code blocks. - ''' - args = [x for x in args if not is_null(x)] - if not args: - return SymbolBLOCK() # Empty block + """ Creates a chain of code blocks. + """ + new_args = [] + args = [x for x in args if not is_null(x)] for x in args: assert isinstance(x, Symbol) + if x.token == 'BLOCK': + new_args.extend(SymbolBLOCK.make_node(*x.children).children) + else: + new_args.append(x) - if args[0].token == 'BLOCK': - args = args[0].children + args[1:] - - if args and args[-1].token == 'BLOCK': - args = args[:-1] + args[-1].children - - result = SymbolBLOCK(*tuple(args)) + result = SymbolBLOCK(*new_args) return result def __getitem__(self, item): diff --git a/symbols/builtin.py b/symbols/builtin.py index 83c2711af..0b7d24980 100644 --- a/symbols/builtin.py +++ b/symbols/builtin.py @@ -18,8 +18,8 @@ class SymbolBUILTIN(Symbol): - ''' Defines an BUILTIN function e.g. INKEY$(), RND() or LEN - ''' + """ Defines an BUILTIN function e.g. INKEY$(), RND() or LEN + """ def __init__(self, lineno, fname, type_=None, *operands): assert isinstance(lineno, int) assert type_ is None or isinstance(type_, SymbolTYPE) @@ -60,22 +60,22 @@ def operands(self, value): @property def size(self): - ''' sizeof(type) - ''' + """ sizeof(type) + """ if self.type_ is None: return 0 return self.type_.size @classmethod def make_node(cls, lineno, fname, func=None, type_=None, *operands): - ''' Creates a node for a unary operation. E.g. -x or LEN(a$) + """ Creates a node for a unary operation. E.g. -x or LEN(a$) Parameters: -func: function used on constant folding when possible -type_: the resulting type (by default, the same as the argument). For example, for LEN (str$), result type is 'u16' and arg type is 'string' - ''' + """ if func is not None and len(operands) == 1: # Try constant-folding if is_number(operands[0]) or is_string(operands[0]): # e.g. ABS(-5) return SymbolNUMBER(func(operands[0].value), type_=type_, lineno=lineno) diff --git a/symbols/funcdecl.py b/symbols/funcdecl.py index 89c52fcf5..07e04a792 100644 --- a/symbols/funcdecl.py +++ b/symbols/funcdecl.py @@ -18,9 +18,10 @@ class SymbolFUNCDECL(Symbol): """ Defines a Function declaration """ - def __init__(self, entry): + def __init__(self, entry, lineno): super(SymbolFUNCDECL, self).__init__() self.entry = entry # Symbol table entry + self.lineno = lineno # Line of this function declaration @property def entry(self): @@ -69,12 +70,12 @@ def mangled(self): return self.entry.mangled @classmethod - def make_node(cls, func_name, lineno): + def make_node(cls, func_name, lineno, type_=None): """ This will return a node with the symbol as a function. """ - entry = global_.SYMBOL_TABLE.declare_func(func_name, lineno) + entry = global_.SYMBOL_TABLE.declare_func(func_name, lineno, type_=type_) if entry is None: return None entry.declared = True - return cls(entry) + return cls(entry, lineno) diff --git a/symbols/nop.py b/symbols/nop.py index 60ef82c3d..2fc06c87e 100644 --- a/symbols/nop.py +++ b/symbols/nop.py @@ -9,12 +9,12 @@ # the GNU General License # ---------------------------------------------------------------------- -from .symbol_ import Symbol +from .block import SymbolBLOCK -class SymbolNOP(Symbol): +class SymbolNOP(SymbolBLOCK): def __init__(self): - Symbol.__init__(self) + super(SymbolNOP, self).__init__() def __bool__(self): return False diff --git a/symbols/number.py b/symbols/number.py index fbfd7a9f4..60b825740 100644 --- a/symbols/number.py +++ b/symbols/number.py @@ -15,6 +15,20 @@ from .symbol_ import Symbol from .type_ import SymbolTYPE from .type_ import Type as TYPE +from .const import SymbolCONST + + +def _get_val(other): + """ Given a Number, a Numeric Constant or a python number return its value + """ + assert isinstance(other, (numbers.Number, SymbolNUMBER, SymbolCONST)) + if isinstance(other, SymbolNUMBER): + return other.value + + if isinstance(other, SymbolCONST): + return other.expr.value + + return other class SymbolNUMBER(Symbol): @@ -79,57 +93,67 @@ def t(self): return str(self) def __eq__(self, other): - if not isinstance(other, (numbers.Number, SymbolNUMBER)): + if not isinstance(other, (numbers.Number, SymbolNUMBER, SymbolCONST)): return False - if isinstance(other, numbers.Number): - return self.value == other - - return self.value == other.value + return self.value == _get_val(other) def __lt__(self, other): - assert isinstance(other, (numbers.Number, SymbolNUMBER)) - - if isinstance(other, numbers.Number): - return self.value < other + return self.value < _get_val(other) - return self.value < other.value + def __le__(self, other): + return self.value <= _get_val(other) def __gt__(self, other): - assert isinstance(other, (numbers.Number, SymbolNUMBER)) + return self.value > _get_val(other) - if isinstance(other, numbers.Number): - return self.value > other - - return self.value > other.value + def __ge__(self, other): + return self.value >= _get_val(other) def __add__(self, other): - assert isinstance(other, (numbers.Number, SymbolNUMBER)) - if isinstance(other, SymbolNUMBER): - return SymbolNUMBER(self.value + other.value, self.lineno) + return SymbolNUMBER(self.value + _get_val(other), self.lineno) - return SymbolNUMBER(self.value + other, self.lineno) + def __radd__(self, other): + return SymbolNUMBER(_get_val(other) + self.value, self.lineno) def __sub__(self, other): - assert isinstance(other, (numbers.Number, SymbolNUMBER)) - if isinstance(other, SymbolNUMBER): - return SymbolNUMBER(self.value - other.value, self.lineno) + return SymbolNUMBER(self.value - _get_val(other), self.lineno) - return SymbolNUMBER(self.value - other, self.lineno) + def __rsub__(self, other): + return SymbolNUMBER(_get_val(other) - self.value, self.lineno) def __mul__(self, other): - assert isinstance(other, (numbers.Number, SymbolNUMBER)) - if isinstance(other, SymbolNUMBER): - return SymbolNUMBER(self.value * other.value, self.lineno) + return SymbolNUMBER(self.value * _get_val(other), self.lineno) - return SymbolNUMBER(self.value * other, self.lineno) + def __rmul__(self, other): + return SymbolNUMBER(_get_val(other) * self.value, self.lineno) def __truediv__(self, other): - assert isinstance(other, (numbers.Number, SymbolNUMBER)) - if isinstance(other, SymbolNUMBER): - return SymbolNUMBER(self.value / other.value, self.lineno) - - return SymbolNUMBER(self.value / other, self.lineno) + return SymbolNUMBER(self.value / _get_val(other), self.lineno) def __div__(self, other): return self.__truediv__(other) + + def __rtruediv__(self, other): + return SymbolNUMBER(_get_val(other) / self.value, self.lineno) + + def __rdiv__(self, other): + return self.__rtruediv__(other) + + def __or__(self, other): + return SymbolNUMBER(self.value | _get_val(other), self.lineno) + + def __ror__(self, other): + return SymbolNUMBER(_get_val(other | self.value), self.lineno) + + def __and__(self, other): + return SymbolNUMBER(self.value & _get_val(other), self.lineno) + + def __rand__(self, other): + return SymbolNUMBER(_get_val(other) & self.value, self.lineno) + + def __mod__(self, other): + return SymbolNUMBER(self.value % _get_val(other), self.lineno) + + def __rmod__(self, other): + return SymbolNUMBER(_get_val(other) % self.value, self.lineno) diff --git a/symbols/sentence.py b/symbols/sentence.py index 1520ddaf5..69bc989a4 100644 --- a/symbols/sentence.py +++ b/symbols/sentence.py @@ -6,21 +6,22 @@ # 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 +# the GNU General License v3 # ---------------------------------------------------------------------- from .symbol_ import Symbol -from api.check import is_null class SymbolSENTENCE(Symbol): - ''' Defines a BASIC SENTENCE object. e.g. 'BORDER'. - ''' - def __init__(self, keyword, *args): - ''' keyword = 'BORDER', or 'PRINT' - ''' - Symbol.__init__(self, *(x for x in args if not is_null(x))) + """ Defines a BASIC SENTENCE object. e.g. 'BORDER'. + """ + def __init__(self, keyword, *args, **kwargs): + """ keyword = 'BORDER', or 'PRINT' + """ + assert not kwargs or 'lineno' in kwargs + super(SymbolSENTENCE, self).__init__(*(x for x in args if x is not None)) self.keyword = keyword + self.lineno = kwargs.get('lineno', None) @property def args(self): @@ -28,6 +29,6 @@ def args(self): @property def token(self): - ''' Sentence takes it's token from the keyword not from it's name - ''' + """ Sentence takes it's token from the keyword not from it's name + """ return self.keyword diff --git a/symbols/strslice.py b/symbols/strslice.py index aa764ab8b..6bf0f902c 100644 --- a/symbols/strslice.py +++ b/symbols/strslice.py @@ -87,6 +87,10 @@ def make_node(cls, lineno, s, lower, upper): upper = TYPECAST.make_node(gl.SYMBOL_TABLE.basic_types[gl.STR_INDEX_TYPE], BINARY.make_node('MINUS', upper, base, lineno=lineno, func=lambda x, y: x - y), lineno) + + if lower is None or upper is None: + return None + if is_number(lower): lo = lower.value if lo < gl.MIN_STRSLICE_IDX: diff --git a/symbols/var.py b/symbols/var.py index 7403893b3..f5af8d746 100644 --- a/symbols/var.py +++ b/symbols/var.py @@ -135,6 +135,7 @@ def to_label(var_instance): from symbols import LABEL var_instance.__class__ = LABEL var_instance.class_ = CLASS.label + var_instance._scope_owner = [] return var_instance @staticmethod diff --git a/tests/api/__init__.py b/tests/api/__init__.py index b6ac7f1da..faa18be5b 100644 --- a/tests/api/__init__.py +++ b/tests/api/__init__.py @@ -1,10 +1,2 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - -import sys -import os -import os.path - -# Initialization -path = os.path.realpath(os.path.join(os.path.join(os.path.dirname(__file__), os.path.pardir), os.path.pardir)) -sys.path.insert(0, path) diff --git a/tests/api/test_config.py b/tests/api/test_config.py new file mode 100644 index 000000000..f708d14b6 --- /dev/null +++ b/tests/api/test_config.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +import sys +from api import config +from api import global_ + + +class TestConfig(unittest.TestCase): + """ Tests api.config initialization + """ + def setUp(self): + config.OPTIONS.reset() + + def test_init(self): + config.init() + self.assertEqual(config.OPTIONS.Debug.value, 0) + self.assertEqual(config.OPTIONS.stdin.value, sys.stdin) + self.assertEqual(config.OPTIONS.stdout.value, sys.stdout) + self.assertEqual(config.OPTIONS.stderr.value, sys.stderr) + self.assertEqual(config.OPTIONS.optimization.value, global_.DEFAULT_OPTIMIZATION_LEVEL) + self.assertEqual(config.OPTIONS.case_insensitive.value, False) + self.assertEqual(config.OPTIONS.array_base.value, 0) + self.assertEqual(config.OPTIONS.byref.value, False) + self.assertEqual(config.OPTIONS.max_syntax_errors.value, global_.DEFAULT_MAX_SYNTAX_ERRORS) + self.assertEqual(config.OPTIONS.string_base.value, 0) + self.assertEqual(config.OPTIONS.memory_map.value, None) + self.assertEqual(config.OPTIONS.bracket.value, False) + self.assertEqual(config.OPTIONS.use_loader.value, False) + self.assertEqual(config.OPTIONS.autorun.value, False) + self.assertEqual(config.OPTIONS.output_file_type.value, 'bin') + self.assertEqual(config.OPTIONS.include_path.value, '') + self.assertEqual(config.OPTIONS.memoryCheck.value, False) + self.assertEqual(config.OPTIONS.strictBool.value, False) + self.assertEqual(config.OPTIONS.arrayCheck.value, False) + self.assertEqual(config.OPTIONS.enableBreak.value, False) + self.assertEqual(config.OPTIONS.emitBackend.value, False) + self.assertEqual(config.OPTIONS.arch.value, 'zx48k') + # private options that cannot be accessed with #pragma + self.assertEqual(config.OPTIONS.option('__DEFINES').value, {}) + self.assertEqual(config.OPTIONS.explicit.value, False) + self.assertEqual(config.OPTIONS.Sinclair.value, False) + self.assertEqual(config.OPTIONS.strict.value, False) + + def test_initted_values(self): + config.init() + self.assertEqual(sorted(config.OPTIONS.options.keys()), ['Debug', + 'Sinclair', + 'StdErrFileName', + '__DEFINES', + 'arch', + 'arrayCheck', + 'array_base', + 'autorun', + 'bracket', + 'byref', + 'case_insensitive', + 'emitBackend', + 'enableBreak', + 'explicit', + 'include_path', + 'inputFileName', + 'max_syntax_errors', + 'memoryCheck', + 'memory_map', + 'optimization', + 'outputFileName', + 'output_file_type', + 'stderr', + 'stdin', + 'stdout', + 'strict', + 'strictBool', + 'string_base', + 'use_loader']) diff --git a/tests/cmdline/__init__.py b/tests/cmdline/__init__.py new file mode 100644 index 000000000..8a90b0f7c --- /dev/null +++ b/tests/cmdline/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import os +import os.path + +path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) +sys.path.insert(0, path) diff --git a/tests/cmdline/empty.bas b/tests/cmdline/empty.bas new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/tests/cmdline/empty.bas @@ -0,0 +1,2 @@ + + diff --git a/tests/cmdline/test_zxb.py b/tests/cmdline/test_zxb.py new file mode 100644 index 000000000..e57935f0a --- /dev/null +++ b/tests/cmdline/test_zxb.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import pytest +import zxb +import os + +PATH = os.path.realpath(os.path.dirname(os.path.abspath(__file__))) + + +class EnsureRemoveFile(object): + """ Ensures a filename is removed if exists after + a block of code is executed + """ + def __init__(self, output_file_name): + self.fname = output_file_name + + def remove_file(self): + if os.path.isfile(self.fname): + os.unlink(self.fname) + + def __enter__(self): + self.remove_file() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.remove_file() + + +@pytest.fixture +def file_bas(): + return os.path.join(PATH, 'empty.bas') + + +@pytest.fixture +def file_bin(): + return os.path.join(PATH, 'empty.bin') + + +def test_compile_only(file_bas, file_bin): + """ Should not generate a file + """ + with EnsureRemoveFile(file_bin): + zxb.main(['--parse-only', file_bas, '-o', file_bin]) + assert not os.path.isfile(file_bin), 'Should not create file "empty.bin"' + + +def test_org_allows_0xnnnn_format(file_bas, file_bin): + """ Should allow hexadecimal format 0x in org + """ + with EnsureRemoveFile(file_bin): + zxb.main(['--parse-only', '--org', '0xC000', file_bas, '-o', file_bin]) + assert zxb.OPTIONS.org.value == 0xC000, 'Should set ORG to 0xC000' diff --git a/tests/functional/00.asm b/tests/functional/00.asm index a67a88f09..6323e744f 100644 --- a/tests/functional/00.asm +++ b/tests/functional/00.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/02.asm b/tests/functional/02.asm index 460452dd8..e52d2a3ae 100644 --- a/tests/functional/02.asm +++ b/tests/functional/02.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/03.asm b/tests/functional/03.asm index 225aebe3f..237c470ee 100644 --- a/tests/functional/03.asm +++ b/tests/functional/03.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/04.asm b/tests/functional/04.asm index 460452dd8..e52d2a3ae 100644 --- a/tests/functional/04.asm +++ b/tests/functional/04.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/05.asm b/tests/functional/05.asm index d1748f559..67c1a812d 100644 --- a/tests/functional/05.asm +++ b/tests/functional/05.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/06.asm b/tests/functional/06.asm index 1e14a3494..d0cc043be 100644 --- a/tests/functional/06.asm +++ b/tests/functional/06.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/07.asm b/tests/functional/07.asm index 05cdd2dd1..2669cd7fc 100644 --- a/tests/functional/07.asm +++ b/tests/functional/07.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0001h diff --git a/tests/functional/08.asm b/tests/functional/08.asm index ddd57e805..118ef5cd6 100644 --- a/tests/functional/08.asm +++ b/tests/functional/08.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _x: DEFB 05h diff --git a/tests/functional/09.asm b/tests/functional/09.asm index c3e25833e..3b2ea1095 100644 --- a/tests/functional/09.asm +++ b/tests/functional/09.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/10.asm b/tests/functional/10.asm index 4dd980305..bb2b17453 100644 --- a/tests/functional/10.asm +++ b/tests/functional/10.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/11.asm b/tests/functional/11.asm index 18b2ced1c..9646a0511 100644 --- a/tests/functional/11.asm +++ b/tests/functional/11.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0001h diff --git a/tests/functional/12.asm b/tests/functional/12.asm index f6cde66f8..5b057e94c 100644 --- a/tests/functional/12.asm +++ b/tests/functional/12.asm @@ -27,7 +27,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/13.asm b/tests/functional/13.asm index dd0e03770..d1021a9c6 100644 --- a/tests/functional/13.asm +++ b/tests/functional/13.asm @@ -27,7 +27,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/15.asm b/tests/functional/15.asm index 88083b6f3..cc027e745 100644 --- a/tests/functional/15.asm +++ b/tests/functional/15.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/16.asm b/tests/functional/16.asm index a30afc56e..1f6d4b612 100644 --- a/tests/functional/16.asm +++ b/tests/functional/16.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/17.asm b/tests/functional/17.asm index fe2b335e5..da6c72adb 100644 --- a/tests/functional/17.asm +++ b/tests/functional/17.asm @@ -31,7 +31,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/18.asm b/tests/functional/18.asm index 795a9995c..fae65d062 100644 --- a/tests/functional/18.asm +++ b/tests/functional/18.asm @@ -33,7 +33,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/19.asm b/tests/functional/19.asm index 70ab7c141..daceb35b0 100644 --- a/tests/functional/19.asm +++ b/tests/functional/19.asm @@ -81,38 +81,40 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "acos.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -128,109 +130,117 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "acos.asm" - + ACOS: ; Computes ACOS using ROM FP-CALC call __FPSTACK_PUSH - + rst 28h ; ROM CALC defb 23h ; ACOS defb 38h ; END CALC - + jp __FPSTACK_POP - + #line 72 "19.bas" #line 1 "asin.asm" - - + + + ASIN: ; Computes ASIN using ROM FP-CALC call __FPSTACK_PUSH - + rst 28h ; ROM CALC defb 22h ; ASIN defb 38h ; END CALC - + jp __FPSTACK_POP - + #line 73 "19.bas" #line 1 "atan.asm" - - + + + ATAN: ; Computes ATAN using ROM FP-CALC call __FPSTACK_PUSH - + rst 28h ; ROM CALC defb 24h ; ATAN defb 38h ; END CALC - + jp __FPSTACK_POP - + #line 74 "19.bas" #line 1 "cos.asm" - - + + + COS: ; Computes COS using ROM FP-CALC call __FPSTACK_PUSH - + rst 28h ; ROM CALC defb 20h ; COS defb 38h ; END CALC - + jp __FPSTACK_POP - + #line 75 "19.bas" #line 1 "exp.asm" - - + + + 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 - + #line 76 "19.bas" #line 1 "logn.asm" - - + + + LN: ; Computes Ln(x) using ROM FP-CALC call __FPSTACK_PUSH - + rst 28h ; ROM CALC defb 20h ; 25h defb 38h ; END CALC - + jp __FPSTACK_POP - + #line 77 "19.bas" #line 1 "sin.asm" - - + + + SIN: ; Computes SIN using ROM FP-CALC call __FPSTACK_PUSH - + rst 28h ; ROM CALC defb 1Fh defb 38h ; END CALC - + jp __FPSTACK_POP - + #line 78 "19.bas" #line 1 "sqrt.asm" - - + + + 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 - + #line 79 "19.bas" #line 1 "storef.asm" + __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 @@ -238,7 +248,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -246,7 +256,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -258,22 +268,23 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 80 "19.bas" #line 1 "tan.asm" - - + + + TAN: ; Computes TAN using ROM FP-CALC call __FPSTACK_PUSH - + rst 28h ; ROM CALC defb 21h ; TAN defb 38h ; END CALC - + jp __FPSTACK_POP - + #line 81 "19.bas" - + ZXBASIC_USER_DATA: _x: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/20.asm b/tests/functional/20.asm index c29f9c95f..b212e0033 100644 --- a/tests/functional/20.asm +++ b/tests/functional/20.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/21.asm b/tests/functional/21.asm index 923866203..6d44fc5a7 100644 --- a/tests/functional/21.asm +++ b/tests/functional/21.asm @@ -32,6 +32,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "storef.asm" + __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 @@ -39,7 +40,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -47,7 +48,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -59,9 +60,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 23 "21.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/22.asm b/tests/functional/22.asm index b1fd0c6f3..aaf00590f 100644 --- a/tests/functional/22.asm +++ b/tests/functional/22.asm @@ -29,7 +29,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _b: DEFB 00 diff --git a/tests/functional/25.asm b/tests/functional/25.asm index 3b5f8858b..a2b7a4386 100644 --- a/tests/functional/25.asm +++ b/tests/functional/25.asm @@ -35,11 +35,12 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "not32.asm" + ; ------------------------------------------------------------- ; 32 bit logical NOT ; ------------------------------------------------------------- - -__NOT32: ; A = ¬A + +__NOT32: ; A = ¬A ld a, d or e or h @@ -47,10 +48,10 @@ __NOT32: ; A = ¬A sub 1 ; Gives CARRY only if 0 sbc a, a; Gives 0 if not carry, FF otherwise ret - - + + #line 26 "25.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/26.asm b/tests/functional/26.asm index e7abd3ade..9f17ad2e3 100644 --- a/tests/functional/26.asm +++ b/tests/functional/26.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/27.asm b/tests/functional/27.asm index 7618aa024..d2a9b4ce9 100644 --- a/tests/functional/27.asm +++ b/tests/functional/27.asm @@ -42,6 +42,7 @@ __LABEL0: DEFB 49h DEFB 43h #line 1 "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$) @@ -52,13 +53,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -66,25 +69,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -93,51 +96,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -145,12 +149,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -158,7 +163,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -166,9 +171,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -176,25 +182,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -203,41 +209,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -245,25 +252,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -272,39 +279,39 @@ __STOP: ; | (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 + ; 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 @@ -312,57 +319,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -374,39 +381,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -414,15 +421,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -447,14 +454,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -462,24 +469,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -487,25 +495,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -514,38 +522,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -554,57 +562,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -613,47 +621,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -663,12 +671,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -687,29 +695,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -718,111 +726,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -838,7 +846,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -846,44 +854,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 30 "27.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/28.asm b/tests/functional/28.asm index b5d8a2c3d..178f0e491 100644 --- a/tests/functional/28.asm +++ b/tests/functional/28.asm @@ -45,6 +45,7 @@ __LABEL0: DEFB 49h DEFB 43h #line 1 "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$) @@ -55,13 +56,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -69,25 +72,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -96,51 +99,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -148,12 +152,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -161,7 +166,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -169,9 +174,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -179,25 +185,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -206,41 +212,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -248,25 +255,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -275,39 +282,39 @@ __STOP: ; | (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 + ; 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 @@ -315,57 +322,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -377,39 +384,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -417,15 +424,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -450,14 +457,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -465,24 +472,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -490,25 +498,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -517,38 +525,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -557,57 +565,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -616,47 +624,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -666,12 +674,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -690,29 +698,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -721,111 +729,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -841,7 +849,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -849,62 +857,63 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 33 "28.bas" #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 34 "28.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/29.asm b/tests/functional/29.asm index e42ed147e..28215e290 100644 --- a/tests/functional/29.asm +++ b/tests/functional/29.asm @@ -51,9 +51,10 @@ __LABEL0: DEFB 49h DEFB 43h #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -61,25 +62,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -88,50 +89,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -139,12 +141,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -152,7 +155,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -160,9 +163,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -170,25 +174,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -197,39 +201,39 @@ __STOP: ; | (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 + ; 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 @@ -237,57 +241,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -299,39 +303,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -339,15 +343,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -372,14 +376,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -387,21 +391,22 @@ __MEM_SUBTRACT: 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 - - + + #line 39 "29.bas" #line 1 "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$) @@ -412,13 +417,15 @@ __MEM_SUBTRACT: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -426,25 +433,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -453,43 +460,44 @@ __MEM_SUBTRACT: ; | (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 + ; 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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -497,25 +505,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -524,38 +532,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -564,57 +572,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -623,47 +631,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -673,12 +681,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -697,29 +705,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -728,111 +736,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -848,7 +856,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -856,138 +864,140 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 40 "29.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -995,51 +1005,51 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 41 "29.bas" - - + + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/30.asm b/tests/functional/30.asm index ae456c69c..29d470df1 100644 --- a/tests/functional/30.asm +++ b/tests/functional/30.asm @@ -34,7 +34,7 @@ _a__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/31.asm b/tests/functional/31.asm index 46a7d5e02..79d791678 100644 --- a/tests/functional/31.asm +++ b/tests/functional/31.asm @@ -43,38 +43,40 @@ _test__leave: pop ix ret #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -90,7 +92,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -99,25 +101,26 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 34 "31.bas" #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -138,10 +141,11 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - + + #line 35 "31.bas" #line 1 "storef.asm" + __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 @@ -149,7 +153,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -157,7 +161,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -169,9 +173,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 36 "31.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/32.asm b/tests/functional/32.asm index 463b933d7..e3bb9e78e 100644 --- a/tests/functional/32.asm +++ b/tests/functional/32.asm @@ -30,9 +30,7 @@ _test: push ix ld ix, 0 add ix, sp - ld a, (ix+5) - inc a - ld (ix+5), a + inc (ix+5) _test__leave: ld sp, ix pop ix @@ -41,7 +39,7 @@ _test__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/33.asm b/tests/functional/33.asm index a74120f96..47677cee4 100644 --- a/tests/functional/33.asm +++ b/tests/functional/33.asm @@ -41,7 +41,7 @@ _test__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/34.asm b/tests/functional/34.asm index a6361474e..4dc3da3e9 100644 --- a/tests/functional/34.asm +++ b/tests/functional/34.asm @@ -42,37 +42,39 @@ _test__leave: exx ret #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -81,35 +83,35 @@ __I8TOFREG: 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 @@ -117,20 +119,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -138,32 +140,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 33 "34.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/35.asm b/tests/functional/35.asm index 6a3cc568e..8be3086e9 100644 --- a/tests/functional/35.asm +++ b/tests/functional/35.asm @@ -55,38 +55,40 @@ _test__leave: exx ret #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -102,7 +104,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -111,43 +113,45 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 46 "35.bas" #line 1 "ploadf.asm" + ; Parameter / Local var load ; A => Offset ; IX = Stack Frame ; RESULT: HL => IX + DE - + #line 1 "iloadf.asm" + ; __FASTCALL__ routine which ; loads a 40 bits floating point into A ED CB ; stored at position pointed by POINTER HL ;A DE, BC <-- ((HL)) - + __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) + ld a, (hl) inc hl ld e, (hl) inc hl @@ -157,48 +161,50 @@ __LOADF: ; Loads a 40 bits FP number from address pointed by HL inc hl ld b, (hl) ret - + #line 7 "ploadf.asm" - + __PLOADF: push ix pop hl add hl, de jp __LOADF - + #line 47 "35.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -207,35 +213,35 @@ __I8TOFREG: 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 @@ -243,20 +249,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -264,32 +270,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 48 "35.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/36.asm b/tests/functional/36.asm index 9879c8b08..d4e9131f1 100644 --- a/tests/functional/36.asm +++ b/tests/functional/36.asm @@ -58,38 +58,40 @@ _test__leave: exx ret #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -105,7 +107,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -114,43 +116,45 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 49 "36.bas" #line 1 "ploadf.asm" + ; Parameter / Local var load ; A => Offset ; IX = Stack Frame ; RESULT: HL => IX + DE - + #line 1 "iloadf.asm" + ; __FASTCALL__ routine which ; loads a 40 bits floating point into A ED CB ; stored at position pointed by POINTER HL ;A DE, BC <-- ((HL)) - + __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) + ld a, (hl) inc hl ld e, (hl) inc hl @@ -160,48 +164,50 @@ __LOADF: ; Loads a 40 bits FP number from address pointed by HL inc hl ld b, (hl) ret - + #line 7 "ploadf.asm" - + __PLOADF: push ix pop hl add hl, de jp __LOADF - + #line 50 "36.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -210,35 +216,35 @@ __I8TOFREG: 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 @@ -246,20 +252,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -267,32 +273,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 51 "36.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/37.asm b/tests/functional/37.asm index 45bdeb647..3f1825c5a 100644 --- a/tests/functional/37.asm +++ b/tests/functional/37.asm @@ -58,38 +58,40 @@ _test__leave: exx ret #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -105,7 +107,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -114,43 +116,45 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 49 "37.bas" #line 1 "ploadf.asm" + ; Parameter / Local var load ; A => Offset ; IX = Stack Frame ; RESULT: HL => IX + DE - + #line 1 "iloadf.asm" + ; __FASTCALL__ routine which ; loads a 40 bits floating point into A ED CB ; stored at position pointed by POINTER HL ;A DE, BC <-- ((HL)) - + __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) + ld a, (hl) inc hl ld e, (hl) inc hl @@ -160,48 +164,50 @@ __LOADF: ; Loads a 40 bits FP number from address pointed by HL inc hl ld b, (hl) ret - + #line 7 "ploadf.asm" - + __PLOADF: push ix pop hl add hl, de jp __LOADF - + #line 50 "37.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -210,35 +216,35 @@ __I8TOFREG: 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 @@ -246,20 +252,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -267,32 +273,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 51 "37.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/38.asm b/tests/functional/38.asm index 4009c862b..f20331a8b 100644 --- a/tests/functional/38.asm +++ b/tests/functional/38.asm @@ -58,38 +58,40 @@ _test__leave: exx ret #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -105,7 +107,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -114,43 +116,45 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 49 "38.bas" #line 1 "ploadf.asm" + ; Parameter / Local var load ; A => Offset ; IX = Stack Frame ; RESULT: HL => IX + DE - + #line 1 "iloadf.asm" + ; __FASTCALL__ routine which ; loads a 40 bits floating point into A ED CB ; stored at position pointed by POINTER HL ;A DE, BC <-- ((HL)) - + __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) + ld a, (hl) inc hl ld e, (hl) inc hl @@ -160,48 +164,50 @@ __LOADF: ; Loads a 40 bits FP number from address pointed by HL inc hl ld b, (hl) ret - + #line 7 "ploadf.asm" - + __PLOADF: push ix pop hl add hl, de jp __LOADF - + #line 50 "38.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -210,35 +216,35 @@ __I8TOFREG: 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 @@ -246,20 +252,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -267,32 +273,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 51 "38.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/39.asm b/tests/functional/39.asm index cc78543fd..53108325f 100644 --- a/tests/functional/39.asm +++ b/tests/functional/39.asm @@ -60,37 +60,39 @@ _test__leave: exx ret #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -99,35 +101,35 @@ __I8TOFREG: 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 @@ -135,20 +137,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -156,32 +158,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 51 "39.bas" - + ZXBASIC_USER_DATA: __LABEL0: DEFB 02h diff --git a/tests/functional/40.asm b/tests/functional/40.asm index d806949aa..6cdea066b 100644 --- a/tests/functional/40.asm +++ b/tests/functional/40.asm @@ -53,37 +53,39 @@ _test__leave: exx ret #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -92,35 +94,35 @@ __I8TOFREG: 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 @@ -128,20 +130,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -149,32 +151,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 44 "40.bas" - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/41.asm b/tests/functional/41.asm index f00ddf62f..8144a35a0 100644 --- a/tests/functional/41.asm +++ b/tests/functional/41.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/42.asm b/tests/functional/42.asm index 8a023d07c..de8806de8 100644 --- a/tests/functional/42.asm +++ b/tests/functional/42.asm @@ -52,7 +52,7 @@ _test__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/43.asm b/tests/functional/43.asm index b47235693..667389118 100644 --- a/tests/functional/43.asm +++ b/tests/functional/43.asm @@ -58,37 +58,39 @@ _test__leave: exx ret #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -97,35 +99,35 @@ __I8TOFREG: 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 @@ -133,20 +135,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -154,32 +156,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 49 "43.bas" - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/44.asm b/tests/functional/44.asm index 13654a194..a08683df0 100644 --- a/tests/functional/44.asm +++ b/tests/functional/44.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/45.asm b/tests/functional/45.asm index 9bf23a5ce..10b3ad26d 100644 --- a/tests/functional/45.asm +++ b/tests/functional/45.asm @@ -53,7 +53,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 01h diff --git a/tests/functional/46.asm b/tests/functional/46.asm index 46afdab1f..e3cbf2ca8 100644 --- a/tests/functional/46.asm +++ b/tests/functional/46.asm @@ -42,9 +42,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -52,27 +53,28 @@ __CALL_BACK__: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -90,44 +92,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -135,23 +137,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -160,13 +161,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -174,75 +173,75 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 33 "46.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/47.asm b/tests/functional/47.asm index b3925394d..b4857103f 100644 --- a/tests/functional/47.asm +++ b/tests/functional/47.asm @@ -69,9 +69,10 @@ _test__leave: pop ix ret #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -79,27 +80,28 @@ _test__leave: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -117,44 +119,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -162,23 +164,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -187,13 +188,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -201,75 +200,75 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 60 "47.bas" - + ZXBASIC_USER_DATA: __LABEL0: DEFB 01h diff --git a/tests/functional/48.asm b/tests/functional/48.asm index f6eaf5ede..1c6daa6c1 100644 --- a/tests/functional/48.asm +++ b/tests/functional/48.asm @@ -53,6 +53,7 @@ __LABEL0: DEFB 49h DEFB 43h #line 1 "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$) @@ -63,13 +64,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -77,25 +80,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -104,51 +107,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -156,12 +160,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -169,7 +174,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -177,9 +182,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -187,25 +193,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -214,41 +220,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -256,25 +263,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -283,39 +290,39 @@ __STOP: ; | (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 + ; 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 @@ -323,57 +330,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -385,39 +392,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -425,15 +432,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -458,14 +465,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -473,24 +480,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -498,25 +506,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -525,38 +533,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -565,57 +573,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -624,47 +632,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -674,12 +682,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -698,29 +706,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -729,111 +737,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -849,7 +857,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -857,141 +865,144 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 41 "48.bas" #line 1 "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. - - - + + + __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 - + #line 42 "48.bas" #line 1 "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 + + ; 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 + ; it in dynamic memory if needed). Returns pointer (HL) to resulting ; string. NULL (0) if no memory for padding. ; - + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 18 "strslice.asm" - - - + + + __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) + 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 @@ -999,19 +1010,19 @@ __STRSLICE_FAST: ; __FASTCALL__ Entry ld b, h ld c, l ; Copy to the end of str ccf ; Clears Carry flag for next subtraction - + __CONT: - ld h, b + 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 @@ -1020,15 +1031,15 @@ __CONT: 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 @@ -1041,26 +1052,26 @@ __CONT: 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 - + ret + + ENDP + #line 43 "48.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/49.asm b/tests/functional/49.asm index e8061dc86..42a6b4f04 100644 --- a/tests/functional/49.asm +++ b/tests/functional/49.asm @@ -53,6 +53,7 @@ __LABEL0: DEFB 49h DEFB 43h #line 1 "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$) @@ -63,13 +64,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -77,25 +80,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -104,51 +107,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -156,12 +160,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -169,7 +174,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -177,9 +182,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -187,25 +193,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -214,41 +220,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -256,25 +263,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -283,39 +290,39 @@ __STOP: ; | (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 + ; 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 @@ -323,57 +330,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -385,39 +392,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -425,15 +432,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -458,14 +465,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -473,24 +480,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -498,25 +506,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -525,38 +533,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -565,57 +573,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -624,47 +632,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -674,12 +682,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -698,29 +706,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -729,111 +737,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -849,7 +857,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -857,141 +865,144 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 41 "49.bas" #line 1 "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. - - - + + + __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 - + #line 42 "49.bas" #line 1 "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 + + ; 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 + ; it in dynamic memory if needed). Returns pointer (HL) to resulting ; string. NULL (0) if no memory for padding. ; - + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 18 "strslice.asm" - - - + + + __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) + 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 @@ -999,19 +1010,19 @@ __STRSLICE_FAST: ; __FASTCALL__ Entry ld b, h ld c, l ; Copy to the end of str ccf ; Clears Carry flag for next subtraction - + __CONT: - ld h, b + 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 @@ -1020,15 +1031,15 @@ __CONT: 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 @@ -1041,26 +1052,26 @@ __CONT: 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 - + ret + + ENDP + #line 43 "49.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/52.asm b/tests/functional/52.asm index 4f8c12f2d..9539c624d 100644 --- a/tests/functional/52.asm +++ b/tests/functional/52.asm @@ -32,7 +32,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0001h diff --git a/tests/functional/54.asm b/tests/functional/54.asm index baab9c327..f783997d2 100644 --- a/tests/functional/54.asm +++ b/tests/functional/54.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 04h diff --git a/tests/functional/55.asm b/tests/functional/55.asm index 4c649bd81..b75d4ec38 100644 --- a/tests/functional/55.asm +++ b/tests/functional/55.asm @@ -37,9 +37,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -47,27 +48,28 @@ __CALL_BACK__: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -85,44 +87,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -130,23 +132,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -155,13 +156,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -169,75 +168,75 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 28 "55.bas" - + ZXBASIC_USER_DATA: _a: DEFB 04h diff --git a/tests/functional/60.asm b/tests/functional/60.asm index 64e9e081e..c8e6cfc0d 100644 --- a/tests/functional/60.asm +++ b/tests/functional/60.asm @@ -36,7 +36,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/61.asm b/tests/functional/61.asm index 64e9e081e..c8e6cfc0d 100644 --- a/tests/functional/61.asm +++ b/tests/functional/61.asm @@ -36,7 +36,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/63.asm b/tests/functional/63.asm index ef497b655..ebb557f41 100644 --- a/tests/functional/63.asm +++ b/tests/functional/63.asm @@ -34,9 +34,7 @@ _test: push ix ld ix, 0 add ix, sp - ld a, (ix+5) - inc a - ld (ix+5), a + inc (ix+5) _test__leave: ld sp, ix pop ix @@ -45,7 +43,7 @@ _test__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/64.asm b/tests/functional/64.asm index f21a32992..a82d1447e 100644 --- a/tests/functional/64.asm +++ b/tests/functional/64.asm @@ -31,7 +31,7 @@ __CALL_BACK__: _test: _test__leave: ret - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/65.asm b/tests/functional/65.asm index e11890586..1421a06ce 100644 --- a/tests/functional/65.asm +++ b/tests/functional/65.asm @@ -30,12 +30,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 _test: - ld a, (ix+5) - inc a - ld (ix+5), a + inc (ix+5) _test__leave: ret - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/66.asm b/tests/functional/66.asm index d0ff0fbeb..a48eeb9fa 100644 --- a/tests/functional/66.asm +++ b/tests/functional/66.asm @@ -36,12 +36,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 _test: - ld a, (ix+5) - inc a - ld (ix+5), a + inc (ix+5) _test__leave: ret - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/70.asm b/tests/functional/70.asm new file mode 100644 index 000000000..d37ea9d70 --- /dev/null +++ b/tests/functional/70.asm @@ -0,0 +1,439 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 10 + ld (_b), a + call __U8TOFREG + call SQRT + call EXP + call LN + res 7, e + call TAN + call COS + call SIN + ld hl, _a + call __STOREF + ld a, (_a) + ld de, (_a + 1) + ld bc, (_a + 3) + call __FTOU32REG + ld a, l + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "cos.asm" + +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 2 "cos.asm" + +COS: ; Computes COS using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 20h ; COS + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 36 "70.bas" +#line 1 "exp.asm" + + + +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 + +#line 37 "70.bas" +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 38 "70.bas" +#line 1 "logn.asm" + + + +LN: ; Computes Ln(x) using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 20h ; 25h + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 39 "70.bas" +#line 1 "sin.asm" + + + +SIN: ; Computes SIN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 1Fh + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 40 "70.bas" +#line 1 "sqrt.asm" + + + +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 + +#line 41 "70.bas" +#line 1 "storef.asm" + +__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 + +#line 42 "70.bas" +#line 1 "tan.asm" + + + +TAN: ; Computes TAN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 21h ; TAN + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 43 "70.bas" +#line 1 "u32tofreg.asm" + + +__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 + +#line 44 "70.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 00 +_a: + DEFB 00, 00, 00, 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/70.bas b/tests/functional/70.bas new file mode 100644 index 000000000..135a91b67 --- /dev/null +++ b/tests/functional/70.bas @@ -0,0 +1,5 @@ + +LET b = 10 +LET a = SIN COS TAN ABS LN EXP SQR b +LET b = a + diff --git a/tests/functional/abs.asm b/tests/functional/abs.asm index 0c5ff046c..3e6dd8206 100644 --- a/tests/functional/abs.asm +++ b/tests/functional/abs.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/abs32.asm b/tests/functional/abs32.asm index 03cb5a591..6c24a568a 100644 --- a/tests/functional/abs32.asm +++ b/tests/functional/abs32.asm @@ -32,44 +32,46 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "abs32.asm" - ; 16 bit signed integer abs value + + ; 16 bit signed integer abs value ; HL = value - + #line 1 "neg32.asm" + __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 - + #line 5 "abs32.asm" - + #line 23 "abs32.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/add16.asm b/tests/functional/add16.asm index 7bc4dc8f7..954ec5f46 100644 --- a/tests/functional/add16.asm +++ b/tests/functional/add16.asm @@ -40,7 +40,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/add16a.asm b/tests/functional/add16a.asm index 142e7ce04..5505dc78b 100644 --- a/tests/functional/add16a.asm +++ b/tests/functional/add16a.asm @@ -33,7 +33,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/add16b.asm b/tests/functional/add16b.asm index 39973cc7f..8906a1745 100644 --- a/tests/functional/add16b.asm +++ b/tests/functional/add16b.asm @@ -40,7 +40,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/add32.asm b/tests/functional/add32.asm index 14d54fa81..eaac19a0b 100644 --- a/tests/functional/add32.asm +++ b/tests/functional/add32.asm @@ -64,7 +64,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/add32a.asm b/tests/functional/add32a.asm index d53c03a7a..a49e545e3 100644 --- a/tests/functional/add32a.asm +++ b/tests/functional/add32a.asm @@ -42,7 +42,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/add32b.asm b/tests/functional/add32b.asm index 95aee6ad6..bb8dc132d 100644 --- a/tests/functional/add32b.asm +++ b/tests/functional/add32b.asm @@ -57,7 +57,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/add8.asm b/tests/functional/add8.asm index fdd2740e7..1aeebd29c 100644 --- a/tests/functional/add8.asm +++ b/tests/functional/add8.asm @@ -40,7 +40,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/add8a.asm b/tests/functional/add8a.asm index f27e18fbb..cc37bd76d 100644 --- a/tests/functional/add8a.asm +++ b/tests/functional/add8a.asm @@ -33,7 +33,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/add8b.asm b/tests/functional/add8b.asm index 93fedfb20..a60eb80d4 100644 --- a/tests/functional/add8b.asm +++ b/tests/functional/add8b.asm @@ -39,7 +39,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/addf16.asm b/tests/functional/addf16.asm index 08a654b88..0514142e9 100644 --- a/tests/functional/addf16.asm +++ b/tests/functional/addf16.asm @@ -64,7 +64,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/addf16a.asm b/tests/functional/addf16a.asm index d53c03a7a..a49e545e3 100644 --- a/tests/functional/addf16a.asm +++ b/tests/functional/addf16a.asm @@ -42,7 +42,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/addf16b.asm b/tests/functional/addf16b.asm index 8d2c5a4db..4fbf5b50f 100644 --- a/tests/functional/addf16b.asm +++ b/tests/functional/addf16b.asm @@ -57,7 +57,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/addstr.asm b/tests/functional/addstr.asm index f1ee0c226..34c350ca2 100644 --- a/tests/functional/addstr.asm +++ b/tests/functional/addstr.asm @@ -65,17 +65,19 @@ __LABEL1: DEFW 0001h DEFB 31h #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -83,25 +85,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -110,40 +112,41 @@ __LABEL1: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -151,25 +154,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -178,39 +181,39 @@ __LABEL1: ; | (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 + ; 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 @@ -218,56 +221,56 @@ __LABEL1: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -276,57 +279,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -335,47 +338,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -385,47 +388,49 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 53 "addstr.bas" #line 1 "strcat.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -433,25 +438,25 @@ __STORE_STR2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -460,50 +465,51 @@ __STORE_STR2: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -511,12 +517,13 @@ __STORE_STR2: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -524,16 +531,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -545,39 +552,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -585,15 +592,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -618,14 +625,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -633,113 +640,114 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "strcat.asm" #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -747,50 +755,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 54 "addstr.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/aloadstr0.asm b/tests/functional/aloadstr0.asm index b33cdaa92..46a9d5c02 100644 --- a/tests/functional/aloadstr0.asm +++ b/tests/functional/aloadstr0.asm @@ -35,10 +35,12 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -46,25 +48,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -73,50 +75,51 @@ __CALL_BACK__: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -124,12 +127,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -137,7 +141,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -145,9 +149,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -155,25 +160,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -182,39 +187,39 @@ __STOP: ; | (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 + ; 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 @@ -222,57 +227,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -284,39 +289,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -324,15 +329,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -357,14 +362,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -372,25 +377,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -399,30 +404,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -430,17 +435,19 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 23 "aloadstr0.bas" #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -448,25 +455,25 @@ __LOADSTR: ; __FASTCALL__ entry ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -475,38 +482,38 @@ __LOADSTR: ; __FASTCALL__ entry ; | (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 + ; 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 @@ -515,57 +522,57 @@ __LOADSTR: ; __FASTCALL__ entry ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -574,47 +581,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -624,43 +631,43 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 24 "aloadstr0.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/aloadstr1.asm b/tests/functional/aloadstr1.asm index 12f0ee994..ebacf0849 100644 --- a/tests/functional/aloadstr1.asm +++ b/tests/functional/aloadstr1.asm @@ -40,9 +40,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -50,27 +51,28 @@ __CALL_BACK__: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -88,44 +90,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -133,23 +135,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -158,13 +159,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -172,79 +171,81 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 28 "aloadstr1.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -252,25 +253,25 @@ __FNMUL2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -279,50 +280,51 @@ __FNMUL2: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -330,12 +332,13 @@ __FNMUL2: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -343,7 +346,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -351,9 +354,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -361,25 +365,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -388,39 +392,39 @@ __STOP: ; | (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 + ; 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 @@ -428,57 +432,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -490,39 +494,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -530,15 +534,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -563,14 +567,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -578,25 +582,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -605,30 +609,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -636,17 +640,19 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 29 "aloadstr1.bas" #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -654,25 +660,25 @@ __LOADSTR: ; __FASTCALL__ entry ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -681,38 +687,38 @@ __LOADSTR: ; __FASTCALL__ entry ; | (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 + ; 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 @@ -721,57 +727,57 @@ __LOADSTR: ; __FASTCALL__ entry ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -780,47 +786,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -830,43 +836,43 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 30 "aloadstr1.bas" - + ZXBASIC_USER_DATA: _c: DEFB 00 diff --git a/tests/functional/alxinho1.bas b/tests/functional/alxinho1.bas new file mode 100644 index 000000000..675a5b539 --- /dev/null +++ b/tests/functional/alxinho1.bas @@ -0,0 +1,4 @@ + + +DIM b = @a(0) + diff --git a/tests/functional/and16.asm b/tests/functional/and16.asm index a5558fd73..3b7d3e507 100644 --- a/tests/functional/and16.asm +++ b/tests/functional/and16.asm @@ -45,22 +45,23 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "and16.asm" + ; 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 - + __AND16: ld a, h or l ret z - + ld a, d or e - ret - + ret + #line 36 "and16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/and32.asm b/tests/functional/and32.asm index 7a0e55f41..b93fc8569 100644 --- a/tests/functional/and32.asm +++ b/tests/functional/and32.asm @@ -55,23 +55,24 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "and32.asm" + ; 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 - + __AND32: ld a, l or h or e or d - sub 1 + sub 1 sbc a - + ld c, a - + pop hl - + pop de ld a, d or e @@ -80,14 +81,14 @@ __AND32: or e sub 1 sbc a - + or c cpl jp (hl) - - + + #line 46 "and32.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/and8.asm b/tests/functional/and8.asm index 9debbe01e..c890d1ef9 100644 --- a/tests/functional/and8.asm +++ b/tests/functional/and8.asm @@ -22,7 +22,10 @@ __START_PROGRAM: ld (_b), a ld hl, (_a - 1) ld a, (_a) - call __AND8 + or a + jr z, __LABEL0 + ld a, h +__LABEL0: ld (_b), a ld hl, 0 ld b, h @@ -40,20 +43,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "and8.asm" - ; FASTCALL boolean and 8 version. - ; result in Accumulator (0 False, not 0 True) -; __FASTCALL__ version (operands: A, H) - ; Performs 8bit and 8bit and returns the boolean - -__AND8: - or a - ret z - ld a, h - ret - -#line 32 "and8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/arden2.asm b/tests/functional/arden2.asm index 866fa9884..7cce41a1c 100644 --- a/tests/functional/arden2.asm +++ b/tests/functional/arden2.asm @@ -45,9 +45,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -55,25 +56,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -82,50 +83,51 @@ __CALL_BACK__: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -133,12 +135,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -146,7 +149,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -154,9 +157,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -164,25 +168,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -191,39 +195,39 @@ __STOP: ; | (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 + ; 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 @@ -231,57 +235,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -293,39 +297,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -333,15 +337,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -366,14 +370,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -381,109 +385,112 @@ __MEM_SUBTRACT: 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 - - + + #line 33 "arden2.bas" #line 1 "chr.asm" + ; CHR$(x, y, x) returns the string CHR$(x) + CHR$(y) + CHR$(z) ; - - - + + + 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 - + 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 - + #line 34 "arden2.bas" #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -491,25 +498,25 @@ __CHR_END: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -518,38 +525,38 @@ __CHR_END: ; | (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 + ; 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 @@ -558,57 +565,57 @@ __CHR_END: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -617,47 +624,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -667,137 +674,139 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 35 "arden2.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -805,50 +814,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 36 "arden2.bas" - + ZXBASIC_USER_DATA: _c: DEFB 00, 00 diff --git a/tests/functional/array00.asm b/tests/functional/array00.asm index 6d0db0f24..db537295a 100644 --- a/tests/functional/array00.asm +++ b/tests/functional/array00.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _bufferx: DEFW 0001h diff --git a/tests/functional/array01.asm b/tests/functional/array01.asm index 56f683953..67927b229 100644 --- a/tests/functional/array01.asm +++ b/tests/functional/array01.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/array02.asm b/tests/functional/array02.asm index 4073622e3..2540b9393 100644 --- a/tests/functional/array02.asm +++ b/tests/functional/array02.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _b: DEFB 00 diff --git a/tests/functional/array03.asm b/tests/functional/array03.asm index bb20aaf13..ca70f21ce 100644 --- a/tests/functional/array03.asm +++ b/tests/functional/array03.asm @@ -35,9 +35,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -45,27 +46,28 @@ __CALL_BACK__: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -83,44 +85,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -128,23 +130,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -153,13 +154,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -167,75 +166,75 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 26 "array03.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00 diff --git a/tests/functional/array04.asm b/tests/functional/array04.asm index 80e5cb258..e24942ab3 100644 --- a/tests/functional/array04.asm +++ b/tests/functional/array04.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/array05.asm b/tests/functional/array05.asm index 119d1bd37..68e6de0ab 100644 --- a/tests/functional/array05.asm +++ b/tests/functional/array05.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/array06.asm b/tests/functional/array06.asm index 3ca57bcaa..b6fc12f28 100644 --- a/tests/functional/array06.asm +++ b/tests/functional/array06.asm @@ -35,9 +35,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -45,27 +46,28 @@ __CALL_BACK__: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -83,44 +85,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -128,23 +130,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -153,13 +154,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -167,75 +166,75 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 26 "array06.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/array07.asm b/tests/functional/array07.asm index fd0837b3b..774a4bc51 100644 --- a/tests/functional/array07.asm +++ b/tests/functional/array07.asm @@ -58,9 +58,10 @@ _test__leave: exx ret #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -68,27 +69,28 @@ _test__leave: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -106,44 +108,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -151,23 +153,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -176,13 +177,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -190,78 +189,79 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 46 "array07.bas" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -269,25 +269,25 @@ __FNMUL2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -296,40 +296,41 @@ __FNMUL2: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -337,25 +338,25 @@ __FNMUL2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -364,39 +365,39 @@ __FNMUL2: ; | (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 + ; 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 @@ -404,56 +405,56 @@ __FNMUL2: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -462,57 +463,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -521,47 +522,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -571,11 +572,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 47 "array07.bas" #line 1 "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$) @@ -586,13 +588,15 @@ __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 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -600,25 +604,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -627,51 +631,52 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -679,12 +684,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -692,7 +698,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -700,9 +706,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -710,25 +717,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -737,40 +744,40 @@ __STOP: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -782,39 +789,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -822,15 +829,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -855,14 +862,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -870,23 +877,23 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -905,29 +912,29 @@ __MEM_SUBTRACT: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -936,111 +943,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -1056,7 +1063,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -1064,44 +1071,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 48 "array07.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/array08.asm b/tests/functional/array08.asm new file mode 100644 index 000000000..174c4493e --- /dev/null +++ b/tests/functional/array08.asm @@ -0,0 +1,1124 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, (_b) + push hl + ld hl, _a + call __ARRAY + ld de, (_s) + call __STORE_STR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/src/zxb/trunk/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 24 "array08.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 70 "realloc.asm" +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 71 "realloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 72 "realloc.asm" + + + ; --------------------------------------------------------------------- + ; 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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 25 "array08.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 00, 00 +_s: + DEFB 00, 00 +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/array08.bas b/tests/functional/array08.bas new file mode 100644 index 000000000..7f39002b1 --- /dev/null +++ b/tests/functional/array08.bas @@ -0,0 +1,6 @@ +DIM a$(10) +DIM b as Uinteger +DIM s as String + +a$(b) = s + diff --git a/tests/functional/array09.asm b/tests/functional/array09.asm index dbe33c68b..9dd88c40b 100644 --- a/tests/functional/array09.asm +++ b/tests/functional/array09.asm @@ -36,9 +36,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -46,27 +47,28 @@ __CALL_BACK__: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -84,44 +86,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -129,23 +131,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -154,13 +155,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -168,75 +167,76 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 24 "array09.bas" #line 1 "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$) @@ -247,13 +247,15 @@ __FNMUL2: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -261,25 +263,25 @@ __FNMUL2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -288,51 +290,52 @@ __FNMUL2: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -340,12 +343,13 @@ __FNMUL2: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -353,7 +357,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -361,9 +365,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -371,25 +376,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -398,41 +403,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -440,25 +446,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -467,39 +473,39 @@ __STOP: ; | (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 + ; 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 @@ -507,57 +513,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -569,39 +575,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -609,15 +615,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -642,14 +648,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -657,24 +663,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -682,25 +689,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -709,38 +716,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -749,57 +756,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -808,47 +815,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -858,12 +865,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -882,29 +889,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -913,111 +920,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -1033,7 +1040,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -1041,44 +1048,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 25 "array09.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/array10.asm b/tests/functional/array10.asm index 817147528..8d6f2b0e0 100644 --- a/tests/functional/array10.asm +++ b/tests/functional/array10.asm @@ -68,9 +68,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -78,27 +79,28 @@ __CALL_BACK__: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -116,44 +118,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -161,23 +163,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -186,13 +187,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -200,75 +199,75 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 59 "array10.bas" - + ZXBASIC_USER_DATA: _tn: DEFB 00 diff --git a/tests/functional/array12.asm b/tests/functional/array12.asm new file mode 100644 index 000000000..42a760e67 --- /dev/null +++ b/tests/functional/array12.asm @@ -0,0 +1,1347 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, (_b) + push hl + ld hl, (_b) + push hl + ld hl, _a + call __ARRAY + ld de, (_s) + call __STORE_STR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/src/zxb/trunk/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 26 "array12.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 70 "realloc.asm" +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 71 "realloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 72 "realloc.asm" + + + ; --------------------------------------------------------------------- + ; 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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 27 "array12.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 00, 00 +_s: + DEFB 00, 00 +_a: + DEFW 0001h + DEFW 000Bh + DEFB 02h + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/array12.bas b/tests/functional/array12.bas new file mode 100644 index 000000000..6b5d7594d --- /dev/null +++ b/tests/functional/array12.bas @@ -0,0 +1,6 @@ +DIM a$(10, 10) +DIM b as Uinteger +DIM s as String + +a$(b, b) = s + diff --git a/tests/functional/array_err.bas b/tests/functional/array_err.bas new file mode 100644 index 000000000..7dd8a6d94 --- /dev/null +++ b/tests/functional/array_err.bas @@ -0,0 +1,3 @@ + +dim test(10,1) as ubyte => {{0,0,0,0,0,0,0,0,0,0,0}} + diff --git a/tests/functional/arraycopy0.asm b/tests/functional/arraycopy0.asm index 098d8c137..96b726098 100644 --- a/tests/functional/arraycopy0.asm +++ b/tests/functional/arraycopy0.asm @@ -40,7 +40,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/arraycopy1.asm b/tests/functional/arraycopy1.asm index 7414822de..b7bf1b8b6 100644 --- a/tests/functional/arraycopy1.asm +++ b/tests/functional/arraycopy1.asm @@ -62,7 +62,7 @@ _Test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _grid: DEFW 0000h diff --git a/tests/functional/arraycopy2.asm b/tests/functional/arraycopy2.asm index 9bcc04b2d..152faba4f 100644 --- a/tests/functional/arraycopy2.asm +++ b/tests/functional/arraycopy2.asm @@ -62,7 +62,7 @@ _Test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _gridcopy: DEFW 0000h diff --git a/tests/functional/arraycopy3.asm b/tests/functional/arraycopy3.asm index a8ea242e9..d5138b680 100644 --- a/tests/functional/arraycopy3.asm +++ b/tests/functional/arraycopy3.asm @@ -75,7 +75,7 @@ _Test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/arrbase1.asm b/tests/functional/arrbase1.asm new file mode 100644 index 000000000..61d89fd7c --- /dev/null +++ b/tests/functional/arrbase1.asm @@ -0,0 +1,1585 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, 1 + ld (_k), hl + jp __LABEL0 +__LABEL3: + ld a, 2 + call __READ + push af + ld hl, 0 + push hl + ld hl, (_k) + dec hl + push hl + ld hl, _c + call __ARRAY + pop af + ld (hl), a +__LABEL4: + ld hl, (_k) + inc hl + ld (_k), hl +__LABEL0: + ld hl, 2 + ld de, (_k) + or a + sbc hl, de + jp nc, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__DATA__0: +__DATA__END: + DEFB 00h +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 48 "arrbase1.bas" +#line 1 "read_restore.asm" + + ;; 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 + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 23 "read_restore.asm" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 29 "read_restore.asm" + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 49 "arrbase1.bas" + +ZXBASIC_USER_DATA: +_k: + DEFB 00, 00 +_c: + DEFW 0001h + DEFW 0003h + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/arrbase1.bas b/tests/functional/arrbase1.bas new file mode 100644 index 000000000..0d60216f6 --- /dev/null +++ b/tests/functional/arrbase1.bas @@ -0,0 +1,10 @@ +' Test with Array Base = 1 +#pragma array_base = 1 + +DIM c(2, 3) as Byte +DIM k as Uinteger + +FOR k=1 TO 2 + READ c(k, 1) +NEXT k + diff --git a/tests/functional/arrcheck.asm b/tests/functional/arrcheck.asm new file mode 100644 index 000000000..d1fae43e2 --- /dev/null +++ b/tests/functional/arrcheck.asm @@ -0,0 +1,389 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, (_b) + push hl + ld hl, 5 + push hl + ld hl, (_b) + push hl + ld hl, 10 + push hl + ld hl, _a + call __ARRAY + ld (hl), 7 + ld hl, (_b) + push hl + ld hl, 5 + push hl + ld hl, (_b) + ld de, 6 + add hl, de + push hl + ld hl, 10 + push hl + ld hl, _a + call __ARRAY + ld a, (hl) + ld (_c), a + ld (_c), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 23 "array.asm" +#line 24 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: + + pop de +#line 49 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + + + ex de, hl + or a + sbc hl, bc + ld a, ERROR_SubscriptWrong + jp c, __ERROR + ex de, hl +#line 59 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 44 "arrcheck.bas" + +ZXBASIC_USER_DATA: +_c: + DEFB 00 +_b: + DEFB 05h + DEFB 00h +_a: + DEFW 0001h + DEFW 0006h + DEFB 01h + 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 + 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 + 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 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/arrcheck.bas b/tests/functional/arrcheck.bas new file mode 100644 index 000000000..51f503a9a --- /dev/null +++ b/tests/functional/arrcheck.bas @@ -0,0 +1,10 @@ +#pragma arrayCheck=true +#define __CHECK_ARRAY_BOUNDARY__ + +DIM a(10, 5) as byte +DIM c as Byte + +DIM b as UInteger = 5 +Let a(b, b) = 7 +Let c = a(b + 6, b) +let c = c diff --git a/tests/functional/arrconst.asm b/tests/functional/arrconst.asm new file mode 100644 index 000000000..3f0ee8e83 --- /dev/null +++ b/tests/functional/arrconst.asm @@ -0,0 +1,61 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, (_p) + ld (_p), hl + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_p: + DEFW (_a + 5) +_a: + DEFW 0001h + DEFW 0003h + DEFB 02h + 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 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/arrconst.bas b/tests/functional/arrconst.bas new file mode 100644 index 000000000..daa169f44 --- /dev/null +++ b/tests/functional/arrconst.bas @@ -0,0 +1,5 @@ + +DIM a(2, 2) as UInteger +DIM p = @a(0, 0) +p = p + diff --git a/tests/functional/arrlabels.asm b/tests/functional/arrlabels.asm index c3e0ddb4e..9093ce1cb 100644 --- a/tests/functional/arrlabels.asm +++ b/tests/functional/arrlabels.asm @@ -34,7 +34,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/arrlabels1.asm b/tests/functional/arrlabels1.asm index d3af099f3..b0400afaf 100644 --- a/tests/functional/arrlabels1.asm +++ b/tests/functional/arrlabels1.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/arrlabels10a.asm b/tests/functional/arrlabels10a.asm index 1592484fa..96e2ca879 100644 --- a/tests/functional/arrlabels10a.asm +++ b/tests/functional/arrlabels10a.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/arrlabels10b.asm b/tests/functional/arrlabels10b.asm index 9eb45276a..6a0a7cd4a 100644 --- a/tests/functional/arrlabels10b.asm +++ b/tests/functional/arrlabels10b.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/arrlabels2.asm b/tests/functional/arrlabels2.asm index 54dd64753..cc8ddb589 100644 --- a/tests/functional/arrlabels2.asm +++ b/tests/functional/arrlabels2.asm @@ -31,7 +31,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/arrlabels3.asm b/tests/functional/arrlabels3.asm index 46aa5c95c..2339ee11c 100644 --- a/tests/functional/arrlabels3.asm +++ b/tests/functional/arrlabels3.asm @@ -40,7 +40,7 @@ _f__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/arrlabels4.asm b/tests/functional/arrlabels4.asm index 4ae827bee..d82ced2cb 100644 --- a/tests/functional/arrlabels4.asm +++ b/tests/functional/arrlabels4.asm @@ -61,7 +61,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/arrlabels5.asm b/tests/functional/arrlabels5.asm index c648c1399..4231f1a9d 100644 --- a/tests/functional/arrlabels5.asm +++ b/tests/functional/arrlabels5.asm @@ -61,7 +61,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/arrlabels6.asm b/tests/functional/arrlabels6.asm index 8d08c3f7f..8f12d6c78 100644 --- a/tests/functional/arrlabels6.asm +++ b/tests/functional/arrlabels6.asm @@ -34,7 +34,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW 0000h diff --git a/tests/functional/arrlabels7.asm b/tests/functional/arrlabels7.asm index 6a2255a19..db4b8f6b1 100644 --- a/tests/functional/arrlabels7.asm +++ b/tests/functional/arrlabels7.asm @@ -61,7 +61,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/arrlabels8.asm b/tests/functional/arrlabels8.asm index 4ae827bee..d82ced2cb 100644 --- a/tests/functional/arrlabels8.asm +++ b/tests/functional/arrlabels8.asm @@ -61,7 +61,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/arrlabels9.asm b/tests/functional/arrlabels9.asm index 4ae827bee..d82ced2cb 100644 --- a/tests/functional/arrlabels9.asm +++ b/tests/functional/arrlabels9.asm @@ -61,7 +61,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 00h diff --git a/tests/functional/asmcolon.asm b/tests/functional/asmcolon.asm new file mode 100644 index 000000000..963759f94 --- /dev/null +++ b/tests/functional/asmcolon.asm @@ -0,0 +1,9 @@ + + LD a, 5 + LD b, 1 + + LD a, 5: LD b, 1 + +TEST: LD a, 5: LD b, 1 + jp TEST + diff --git a/tests/functional/asmcolon.bin b/tests/functional/asmcolon.bin new file mode 100644 index 000000000..24a5c979f Binary files /dev/null and b/tests/functional/asmcolon.bin differ diff --git a/tests/functional/asmerror2.asm b/tests/functional/asmerror2.asm new file mode 100644 index 000000000..b21ffbc04 --- /dev/null +++ b/tests/functional/asmerror2.asm @@ -0,0 +1,3 @@ + + LD a, @# + diff --git a/tests/functional/asmlabel1.asm b/tests/functional/asmlabel1.asm new file mode 100644 index 000000000..f7c2fccee --- /dev/null +++ b/tests/functional/asmlabel1.asm @@ -0,0 +1,4 @@ + +label : + jp label + diff --git a/tests/functional/asmlabel1.bin b/tests/functional/asmlabel1.bin new file mode 100644 index 000000000..894ac62ea Binary files /dev/null and b/tests/functional/asmlabel1.bin differ diff --git a/tests/functional/asmlabel2.asm b/tests/functional/asmlabel2.asm new file mode 100644 index 000000000..3d90bd111 --- /dev/null +++ b/tests/functional/asmlabel2.asm @@ -0,0 +1,5 @@ + +; test label: instruction + +test: jp test + diff --git a/tests/functional/asmlabel2.bin b/tests/functional/asmlabel2.bin new file mode 100644 index 000000000..894ac62ea Binary files /dev/null and b/tests/functional/asmlabel2.bin differ diff --git a/tests/functional/asmprepro.asm b/tests/functional/asmprepro.asm new file mode 100644 index 000000000..ba916ba87 --- /dev/null +++ b/tests/functional/asmprepro.asm @@ -0,0 +1,17 @@ +; Test recursive inclusion in ASM files + +#ifndef __I1__ +# ifndef __I2__ +# define __I2__ +nop ; I2 +# include "asmprepro.asm" +# endif +# define __I1__ +nop ; I1 +# include "asmprepro.asm" +# undef __I1__ +nop +#endif + + + diff --git a/tests/functional/asmprepro.bin b/tests/functional/asmprepro.bin new file mode 100644 index 000000000..40b450dd9 Binary files /dev/null and b/tests/functional/asmprepro.bin differ diff --git a/tests/functional/asmproc04.asm b/tests/functional/asmproc04.asm index b29b255cb..e10c6a742 100644 --- a/tests/functional/asmproc04.asm +++ b/tests/functional/asmproc04.asm @@ -5,7 +5,7 @@ testLabel: NAMESPACE other anotherTest: -Namespace default +Namespace . jp test.testLabel diff --git a/tests/functional/asmproc06a.asm b/tests/functional/asmproc06a.asm index 77e396f61..d3499663a 100644 --- a/tests/functional/asmproc06a.asm +++ b/tests/functional/asmproc06a.asm @@ -1,5 +1,5 @@ -; This label should be used in favour of the local one +; The first testLlabel should be used in favour of the local one ; because the jp instruction uses it before declaring it ; local NAMESPACE test @@ -17,7 +17,7 @@ testLabel: ENDP jp testLabel -NAMESPACE default +NAMESPACE . anotherTest: diff --git a/tests/functional/astore16.asm b/tests/functional/astore16.asm index 6e75a7fd0..eab6b4224 100644 --- a/tests/functional/astore16.asm +++ b/tests/functional/astore16.asm @@ -37,9 +37,8 @@ __LABEL3: call __PRINTU16 call PRINT_EOL __LABEL4: - ld a, (_i) - inc a - ld (_i), a + ld hl, _i + inc (hl) __LABEL0: ld a, 15 ld hl, (_i - 1) @@ -63,9 +62,10 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -73,27 +73,28 @@ __CALL_BACK__: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -111,44 +112,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -156,23 +157,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -181,13 +181,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -195,84 +193,86 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - -#line 53 "astore16.bas" + +#line 52 "astore16.bas" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -280,45 +280,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -331,41 +332,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -373,12 +376,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -386,50 +390,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -438,15 +443,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -454,26 +461,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -487,44 +494,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -535,27 +543,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -563,27 +572,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -592,81 +602,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -675,32 +687,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -710,7 +722,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -718,19 +730,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -740,7 +753,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -751,18 +764,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -772,7 +786,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -783,18 +797,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -804,7 +819,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -817,21 +832,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -840,79 +856,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -921,75 +937,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -999,7 +1015,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1011,16 +1027,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1034,49 +1050,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1087,17 +1103,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1110,27 +1126,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1138,10 +1154,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1158,80 +1174,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1248,8 +1264,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1273,17 +1289,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1294,7 +1310,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1304,21 +1320,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1337,9 +1353,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1364,30 +1380,33 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - -#line 54 "astore16.bas" + + +#line 53 "astore16.bas" #line 1 "printu16.asm" + #line 1 "printi16.asm" + #line 1 "printnum.asm" - - - + + + + __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 @@ -1395,28 +1414,30 @@ __PRINTU_CONT: pop bc djnz __PRINTU_CONT ret - + ENDP - - + + __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER mus preserve registers ld a, '-' jp __PRINT_DIGIT - + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - + + #line 2 "printi16.asm" #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -1426,23 +1447,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -1451,46 +1472,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -1498,75 +1519,75 @@ __DIVI16_FAST: 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 + + 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 - + #line 3 "printi16.asm" - - - + + + __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 - + #line 2 "printu16.asm" - -#line 55 "astore16.bas" - + +#line 54 "astore16.bas" + ZXBASIC_USER_DATA: _i: DEFB 00 diff --git a/tests/functional/ataddr.asm b/tests/functional/ataddr.asm index 59e3a2856..85167e629 100644 --- a/tests/functional/ataddr.asm +++ b/tests/functional/ataddr.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/atfunc0.asm b/tests/functional/atfunc0.asm index c26dd2d67..1138a606a 100644 --- a/tests/functional/atfunc0.asm +++ b/tests/functional/atfunc0.asm @@ -36,7 +36,7 @@ _myfunc__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/atfunc1.asm b/tests/functional/atfunc1.asm index c26dd2d67..1138a606a 100644 --- a/tests/functional/atfunc1.asm +++ b/tests/functional/atfunc1.asm @@ -36,7 +36,7 @@ _myfunc__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/atlabel.asm b/tests/functional/atlabel.asm index ebad8613c..9d44a7668 100644 --- a/tests/functional/atlabel.asm +++ b/tests/functional/atlabel.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _radians: _a: diff --git a/tests/functional/atlabel1.asm b/tests/functional/atlabel1.asm index 23784599c..665122fe4 100644 --- a/tests/functional/atlabel1.asm +++ b/tests/functional/atlabel1.asm @@ -34,6 +34,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "storef.asm" + __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 @@ -41,7 +42,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -49,7 +50,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -61,9 +62,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 25 "atlabel1.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/atlabel2.asm b/tests/functional/atlabel2.asm index 522be4ea1..646bfb4b5 100644 --- a/tests/functional/atlabel2.asm +++ b/tests/functional/atlabel2.asm @@ -40,7 +40,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/atlabel3.asm b/tests/functional/atlabel3.asm index 2a2c6de76..b19fdadd5 100644 --- a/tests/functional/atlabel3.asm +++ b/tests/functional/atlabel3.asm @@ -48,7 +48,7 @@ _test2__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/atoloduplbl.asm b/tests/functional/atoloduplbl.asm new file mode 100644 index 000000000..62b99ae44 --- /dev/null +++ b/tests/functional/atoloduplbl.asm @@ -0,0 +1,4 @@ + +SetSubScreen: +SetSubScreen: +ld (ix+5), 1 diff --git a/tests/functional/attr.asm b/tests/functional/attr.asm index 9880b812d..fb6490f9e 100644 --- a/tests/functional/attr.asm +++ b/tests/functional/attr.asm @@ -42,14 +42,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -57,35 +60,35 @@ __CALL_BACK__: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 6 "copy_attr.asm" - + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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) - -#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -94,14 +97,14 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "bold.asm" - + BOLD: PROC - + and 1 rlca rlca @@ -111,7 +114,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -122,23 +125,24 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 33 "attr.bas" - + #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -146,37 +150,38 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 35 "attr.bas" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + 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 @@ -190,41 +195,42 @@ __SET_INK2: 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 - + #line 36 "attr.bas" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register - - - + + + 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 @@ -234,7 +240,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -242,39 +248,40 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 37 "attr.bas" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -285,16 +292,16 @@ __SET_PAPER2: 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 - + #line 38 "attr.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/bad_sigil.bas b/tests/functional/bad_sigil.bas new file mode 100644 index 000000000..1fceb3a99 --- /dev/null +++ b/tests/functional/bad_sigil.bas @@ -0,0 +1,5 @@ + +sub x(y$ as Float) + y = "5" +end sub + diff --git a/tests/functional/band16.asm b/tests/functional/band16.asm index d6b5bd4bc..adff84e28 100644 --- a/tests/functional/band16.asm +++ b/tests/functional/band16.asm @@ -56,27 +56,28 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "band16.asm" + ; vim:ts=4:et: ; FASTCALL bitwise and16 version. - ; result in hl + ; result in hl ; __FASTCALL__ version (operands: A, H) ; Performs 16bit or 16bit and returns the boolean ; Input: HL, DE ; Output: HL <- HL AND DE - + __BAND16: ld a, h and d ld h, a - + ld a, l and e ld l, a - - ret - + + ret + #line 47 "band16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/band32.asm b/tests/functional/band32.asm index e8dacc5ad..0c4bb8869 100644 --- a/tests/functional/band32.asm +++ b/tests/functional/band32.asm @@ -90,44 +90,45 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "band32.asm" + ; 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 - + __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 - + #line 81 "band32.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/band8.asm b/tests/functional/band8.asm index f0e0e2efd..f09da5ecd 100644 --- a/tests/functional/band8.asm +++ b/tests/functional/band8.asm @@ -13,9 +13,8 @@ __START_PROGRAM: ld a, (_a) xor a ld (_b), a - ld h, 1 ld a, (_a) - and h + and 1 ld (_b), a ld a, (_a) ld l, a @@ -54,20 +53,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "and8.asm" - ; FASTCALL boolean and 8 version. - ; result in Accumulator (0 False, not 0 True) -; __FASTCALL__ version (operands: A, H) - ; Performs 8bit and 8bit and returns the boolean - -__AND8: - or a - ret z - ld a, h - ret - -#line 46 "band8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/baspreprocerr1.bas b/tests/functional/baspreprocerr1.bas new file mode 100644 index 000000000..cc839fee4 --- /dev/null +++ b/tests/functional/baspreprocerr1.bas @@ -0,0 +1,2 @@ +#include + diff --git a/tests/functional/baspreprocerr2.bas b/tests/functional/baspreprocerr2.bas new file mode 100644 index 000000000..1157a8e89 --- /dev/null +++ b/tests/functional/baspreprocerr2.bas @@ -0,0 +1,2 @@ +#define @ + diff --git a/tests/functional/bin00.asm b/tests/functional/bin00.asm index 2f44c47cb..f106cb8c4 100644 --- a/tests/functional/bin00.asm +++ b/tests/functional/bin00.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _c: DEFB 00 diff --git a/tests/functional/bin01.asm b/tests/functional/bin01.asm new file mode 100644 index 000000000..c708b31ab --- /dev/null +++ b/tests/functional/bin01.asm @@ -0,0 +1,39 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + xor a + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/bin01.bas b/tests/functional/bin01.bas new file mode 100644 index 000000000..ccde327e2 --- /dev/null +++ b/tests/functional/bin01.bas @@ -0,0 +1,3 @@ +REM BIN (alone) is like BIN 0 +LET a = BIN + diff --git a/tests/functional/bin02.bas b/tests/functional/bin02.bas new file mode 100644 index 000000000..3447ea666 --- /dev/null +++ b/tests/functional/bin02.bas @@ -0,0 +1,2 @@ + +LET a = BIN a = a + 1 diff --git a/tests/functional/bin03.asm b/tests/functional/bin03.asm new file mode 100644 index 000000000..50237cf7b --- /dev/null +++ b/tests/functional/bin03.asm @@ -0,0 +1,41 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + xor a + ld (_a), a + inc a + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/bin03.bas b/tests/functional/bin03.bas new file mode 100644 index 000000000..ff2a79875 --- /dev/null +++ b/tests/functional/bin03.bas @@ -0,0 +1,2 @@ + +LET a = BIN: a = a + 1 diff --git a/tests/functional/bitwise.asm b/tests/functional/bitwise.asm index 6e37840a1..dbaee72ca 100644 --- a/tests/functional/bitwise.asm +++ b/tests/functional/bitwise.asm @@ -41,20 +41,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "and8.asm" - ; FASTCALL boolean and 8 version. - ; result in Accumulator (0 False, not 0 True) -; __FASTCALL__ version (operands: A, H) - ; Performs 8bit and 8bit and returns the boolean - -__AND8: - or a - ret z - ld a, h - ret - -#line 33 "bitwise.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/bnot16.asm b/tests/functional/bnot16.asm index ad032b49b..9bc963536 100644 --- a/tests/functional/bnot16.asm +++ b/tests/functional/bnot16.asm @@ -38,27 +38,28 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "bnot16.asm" + ; 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 - +; Output: HL <- NOT HL + __BNOT16: ld a, h cpl ld h, a - + ld a, l cpl ld l, a - - ret - + + ret + #line 29 "bnot16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/bor16.asm b/tests/functional/bor16.asm index 54f534b3b..5b74fdfe4 100644 --- a/tests/functional/bor16.asm +++ b/tests/functional/bor16.asm @@ -56,6 +56,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "bor16.asm" + ; vim:ts=4:et: ; FASTCALL bitwise or 16 version. ; result in HL @@ -63,20 +64,20 @@ __CALL_BACK__: ; Performs 16bit or 16bit and returns the boolean ; Input: HL, DE ; Output: HL <- HL OR DE - + __BOR16: ld a, h or d ld h, a - + ld a, l or e ld l, a - - ret - + + ret + #line 47 "bor16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/bor32.asm b/tests/functional/bor32.asm index 9f264a902..bb478fa08 100644 --- a/tests/functional/bor32.asm +++ b/tests/functional/bor32.asm @@ -90,44 +90,45 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "bor32.asm" + ; 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 - + __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 - + #line 81 "bor32.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/bor8.asm b/tests/functional/bor8.asm index 3dcc678d5..1b030e2ac 100644 --- a/tests/functional/bor8.asm +++ b/tests/functional/bor8.asm @@ -12,9 +12,8 @@ __START_PROGRAM: ei ld a, (_a) ld (_b), a - ld h, 1 ld a, (_a) - or h + or 1 ld (_b), a ld a, (_a) ld a, 0FFh @@ -48,7 +47,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/bound00.asm b/tests/functional/bound00.asm index 68ff1b7f6..aef38d819 100644 --- a/tests/functional/bound00.asm +++ b/tests/functional/bound00.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/bound01.asm b/tests/functional/bound01.asm index 8b5a86863..bfcdd8493 100644 --- a/tests/functional/bound01.asm +++ b/tests/functional/bound01.asm @@ -34,7 +34,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/bound02.asm b/tests/functional/bound02.asm index 22abd845c..ae10931b4 100644 --- a/tests/functional/bound02.asm +++ b/tests/functional/bound02.asm @@ -52,37 +52,38 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "bound.asm" + ; --------------------------------------------------------- ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) -; http://www.boriel.com +; http://www.boriel.com ; ; ZX BASIC Compiler http://www.zxbasic.net ; This code is released under the BSD License ; --------------------------------------------------------- - + ; Implements bothe the LBOUND(array, N) and RBOUND(array, N) function - + ; Parameters: ; HL = N (dimension) ; [stack - 2] -> LBound table for the var ; Returns entry [N] in HL - + __BOUND: add hl, hl ; hl *= 2 ex de, hl pop hl ex (sp), hl ; __CALLEE - + 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 - + #line 43 "bound02.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00 diff --git a/tests/functional/bound03.asm b/tests/functional/bound03.asm index d91f0cdad..fa79cd92b 100644 --- a/tests/functional/bound03.asm +++ b/tests/functional/bound03.asm @@ -52,37 +52,38 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "bound.asm" + ; --------------------------------------------------------- ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) -; http://www.boriel.com +; http://www.boriel.com ; ; ZX BASIC Compiler http://www.zxbasic.net ; This code is released under the BSD License ; --------------------------------------------------------- - + ; Implements bothe the LBOUND(array, N) and RBOUND(array, N) function - + ; Parameters: ; HL = N (dimension) ; [stack - 2] -> LBound table for the var ; Returns entry [N] in HL - + __BOUND: add hl, hl ; hl *= 2 ex de, hl pop hl ex (sp), hl ; __CALLEE - + 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 - + #line 43 "bound03.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00 diff --git a/tests/functional/bound04.asm b/tests/functional/bound04.asm index f64ebf76e..73c614097 100644 --- a/tests/functional/bound04.asm +++ b/tests/functional/bound04.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _c: DEFB 00, 00 diff --git a/tests/functional/break.asm b/tests/functional/break.asm new file mode 100644 index 000000000..5d518b278 --- /dev/null +++ b/tests/functional/break.asm @@ -0,0 +1,144 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__10: +#line 4 + push hl +#line 5 + ld hl, 4 + call CHECK_BREAK +__LABEL__20: + ld a, 1 + ld (_a), a +#line 5 + push hl +#line 6 + ld hl, 5 + call CHECK_BREAK +__LABEL__30: + ld a, 2 + ld (_a), a + inc a + ld (_a), a +#line 6 + push hl +#line 7 + ld hl, 6 + call CHECK_BREAK + ld a, 40 + ld (_a), a +#line 10 + push hl +#line 11 + ld hl, 10 + call CHECK_BREAK + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "break.asm" + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 2 "break.asm" + + + ; 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. + +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 + +#line 49 "break.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/break.bas b/tests/functional/break.bas new file mode 100644 index 000000000..baa7aecce --- /dev/null +++ b/tests/functional/break.bas @@ -0,0 +1,10 @@ +#pragma enableBreak = true +:REM Keyboard BREAK interrupt generation + +10 DIM a as UByte +20 a = 1 +30 a = 2: a = a + 1 + + + +a = 40 diff --git a/tests/functional/britlion0.asm b/tests/functional/britlion0.asm index 6e4797400..43b68f937 100644 --- a/tests/functional/britlion0.asm +++ b/tests/functional/britlion0.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _num: DEFB 00, 00, 00, 00 diff --git a/tests/functional/bxor16.asm b/tests/functional/bxor16.asm index 1c5b6241e..da9d2da7d 100644 --- a/tests/functional/bxor16.asm +++ b/tests/functional/bxor16.asm @@ -56,6 +56,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "bxor16.asm" + ; vim:ts=4:et: ; FASTCALL bitwise xor 16 version. ; result in Accumulator (0 False, not 0 True) @@ -63,25 +64,26 @@ __CALL_BACK__: ; Performs 16bit xor 16bit and returns the boolean ; Input: HL, DE ; Output: HL <- HL XOR DE - + __BXOR16: ld a, h xor d ld h, a - + ld a, l xor e ld l, a - - ret - + + ret + #line 47 "bxor16.bas" #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -91,9 +93,9 @@ __NEGHL: ld h, a inc hl ret - + #line 48 "bxor16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/bxor32.asm b/tests/functional/bxor32.asm index cfbee71f6..ef498c650 100644 --- a/tests/functional/bxor32.asm +++ b/tests/functional/bxor32.asm @@ -90,44 +90,45 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "bxor32.asm" + ; 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 - + __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 dddress 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 - + #line 81 "bxor32.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/bxor8.asm b/tests/functional/bxor8.asm index b902d78b2..d0acae34d 100644 --- a/tests/functional/bxor8.asm +++ b/tests/functional/bxor8.asm @@ -48,7 +48,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/byref32.asm b/tests/functional/byref32.asm index 1b59e1014..4a8ed8fd1 100644 --- a/tests/functional/byref32.asm +++ b/tests/functional/byref32.asm @@ -61,13 +61,14 @@ _test__leave: exx ret #line 1 "iload32.asm" + ; __FASTCALL__ routine which ; loads a 32 bits integer into DE,HL ; stored at position pointed by POINTER HL ; DE,HL <-- (HL) - + __ILOAD32: - ld e, (hl) + ld e, (hl) inc hl ld d, (hl) inc hl @@ -77,23 +78,25 @@ __ILOAD32: ld l, a ex de, hl ret - + #line 52 "byref32.bas" #line 1 "pistore32.asm" + #line 1 "store32.asm" + __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 @@ -103,12 +106,12 @@ __STORE32: ; Stores the given integer in DEBC at address HL inc hl ld (hl), d ret - + #line 2 "pistore32.asm" - + ; The content of this file has been moved to "store32.asm" #line 53 "byref32.bas" - + ZXBASIC_USER_DATA: _y: DEFB 00, 00, 00, 00 diff --git a/tests/functional/byte_neq.asm b/tests/functional/byte_neq.asm index 2b920a002..4adda95d9 100644 --- a/tests/functional/byte_neq.asm +++ b/tests/functional/byte_neq.asm @@ -13,9 +13,8 @@ __START_PROGRAM: ld a, (_a) sub 5 jp z, __LABEL1 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL1: ld hl, 0 ld b, h @@ -33,7 +32,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/byval32.asm b/tests/functional/byval32.asm index 897336855..027abe821 100644 --- a/tests/functional/byval32.asm +++ b/tests/functional/byval32.asm @@ -63,20 +63,22 @@ _test__leave: exx ret #line 1 "pstore32.asm" + #line 1 "store32.asm" + __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 @@ -86,9 +88,9 @@ __STORE32: ; Stores the given integer in DEBC at address HL inc hl ld (hl), d ret - + #line 2 "pstore32.asm" - + ; Stores a 32 bit integer number (DE,HL) at (IX + BC) __PSTORE32: push hl @@ -98,7 +100,7 @@ __PSTORE32: pop bc jp __STORE32 #line 54 "byval32.bas" - + ZXBASIC_USER_DATA: _y: DEFB 00, 00, 00, 00 diff --git a/tests/functional/castF16toF.asm b/tests/functional/castF16toF.asm index 82f8f720e..1deb6dc9f 100644 --- a/tests/functional/castF16toF.asm +++ b/tests/functional/castF16toF.asm @@ -32,40 +32,43 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "f16tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "f16tofreg.asm" #line 1 "u32tofreg.asm" - + + __I8TOFREG: ld l, a rlca @@ -73,35 +76,35 @@ __I8TOFREG: 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 @@ -109,20 +112,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -130,53 +133,53 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 3 "f16tofreg.asm" - + __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 @@ -184,25 +187,26 @@ __F16TOFREG2: ; Converts an unsigned 32 bit integer (DEHL) 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 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 - + #line 23 "castF16toF.bas" #line 1 "storef.asm" + __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 @@ -210,7 +214,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -218,7 +222,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -230,9 +234,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 24 "castF16toF.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00h diff --git a/tests/functional/chr.asm b/tests/functional/chr.asm index 72155f35f..8d3a16584 100644 --- a/tests/functional/chr.asm +++ b/tests/functional/chr.asm @@ -44,13 +44,15 @@ __LABEL0: DEFW 0001h DEFB 21h #line 1 "chr.asm" + ; CHR$(x, y, x) returns the string CHR$(x) + CHR$(y) + CHR$(z) ; - + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -58,25 +60,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -85,50 +87,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -136,12 +139,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -149,7 +153,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -157,9 +161,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -167,25 +172,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -194,39 +199,39 @@ __STOP: ; | (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 + ; 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 @@ -234,57 +239,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -296,39 +301,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -336,15 +341,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -369,14 +374,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -384,93 +389,94 @@ __MEM_SUBTRACT: 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 - - + + #line 5 "chr.asm" - + 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 - + 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 - + #line 32 "chr.bas" #line 1 "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$) @@ -481,13 +487,15 @@ __CHR_END: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -495,25 +503,25 @@ __CHR_END: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -522,43 +530,44 @@ __CHR_END: ; | (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 + ; 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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -566,25 +575,25 @@ __CHR_END: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -593,38 +602,38 @@ __CHR_END: ; | (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 + ; 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 @@ -633,57 +642,57 @@ __CHR_END: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -692,47 +701,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -742,12 +751,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -766,29 +775,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -797,111 +806,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -917,7 +926,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -925,85 +934,86 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 33 "chr.bas" #line 1 "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. - - - + + + __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 - + #line 34 "chr.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/chr0.asm b/tests/functional/chr0.asm index 77b4db652..6dac9c5e8 100644 --- a/tests/functional/chr0.asm +++ b/tests/functional/chr0.asm @@ -43,13 +43,15 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "chr.asm" + ; CHR$(x, y, x) returns the string CHR$(x) + CHR$(y) + CHR$(z) ; - + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -57,25 +59,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -84,50 +86,51 @@ __CALL_BACK__: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -135,12 +138,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -148,7 +152,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -156,9 +160,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -166,25 +171,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -193,39 +198,39 @@ __STOP: ; | (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 + ; 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 @@ -233,57 +238,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -295,39 +300,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -335,15 +340,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -368,14 +373,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -383,104 +388,106 @@ __MEM_SUBTRACT: 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 - - + + #line 5 "chr.asm" - + 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 - + 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 - + #line 31 "chr0.bas" #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -488,25 +495,25 @@ __CHR_END: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -515,38 +522,38 @@ __CHR_END: ; | (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 + ; 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 @@ -555,57 +562,57 @@ __CHR_END: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -614,47 +621,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -664,43 +671,43 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 32 "chr0.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/chr1.asm b/tests/functional/chr1.asm index 4434ec7b6..9f3ca5b64 100644 --- a/tests/functional/chr1.asm +++ b/tests/functional/chr1.asm @@ -38,6 +38,7 @@ __LABEL0: DEFB 41h DEFB 21h #line 1 "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$) @@ -48,13 +49,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -62,25 +65,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -89,51 +92,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -141,12 +145,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -154,7 +159,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -162,9 +167,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -172,25 +178,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -199,41 +205,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -241,25 +248,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -268,39 +275,39 @@ __STOP: ; | (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 + ; 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 @@ -308,57 +315,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -370,39 +377,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -410,15 +417,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -443,14 +450,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -458,24 +465,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -483,25 +491,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -510,38 +518,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -550,57 +558,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -609,47 +617,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -659,12 +667,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -683,29 +691,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -714,111 +722,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -834,7 +842,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -842,44 +850,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 26 "chr1.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/circle.asm b/tests/functional/circle.asm index 4f0338724..163fb707a 100644 --- a/tests/functional/circle.asm +++ b/tests/functional/circle.asm @@ -85,23 +85,25 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "circle.asm" + ; Bresenham's like circle algorithm ; best known as Middle Point Circle drawing algorithm - + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -109,12 +111,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -122,7 +125,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -130,19 +133,22 @@ __STOP: ret #line 5 "circle.asm" #line 1 "plot.asm" + ; 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 - - + + #line 1 "in_screen.asm" + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -150,74 +156,75 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 2 "in_screen.asm" - - + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 9 "plot.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -230,48 +237,48 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 10 "plot.asm" - + 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 - + ld c, h + 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 @@ -279,7 +286,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) res 6, h ; Starts from 0 ld bc, (SCREEN_ADDR) add hl, bc ; Now current offset - + ld b, a inc b ld a, 0FEh @@ -287,7 +294,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) __PLOT_LOOP: rrca djnz __PLOT_LOOP - + ld b, a ld a, (P_FLAG) ld c, a @@ -295,18 +302,18 @@ __PLOT_LOOP: 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 - + ;; gets ATTR position with offset given in SCREEN_ADDR ld a, h rrca @@ -317,36 +324,36 @@ __PLOT_END: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR jp PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + __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 + PIXEL_ADDR EQU 22ACh COORDS EQU 5C7Dh ENDP #line 6 "circle.asm" - - + + ; Draws a circle at X, Y of radius R ; X, Y on the Stack, R in accumulator (Byte) - + PROC LOCAL __CIRCLE_ERROR LOCAL __CIRCLE_LOOP LOCAL __CIRCLE_NEXT - + __CIRCLE_ERROR: jp __OUT_OF_SCREEN_ERR ;; __CIRCLE_ERROR EQU __OUT_OF_SCREEN_ERR ;; __CIRCLE_ERROR: ;; ; Jumps here if out of screen ;; scf ; Always sets carry Flag - ;; + ;; ;; ld a, ERROR_OutOfScreen ;; ld (ERR_NR), a ;; ret @@ -356,95 +363,95 @@ CIRCLE: pop de ; D = Y ex (sp), hl ; __CALLEE__ convention ld e, h ; E = X - - - ld h, a ; H = R + + + ld h, a ; H = R 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 + 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 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 @@ -454,11 +461,11 @@ __CIRCLE_NEXT: ld h, a pop af ex af, af' - - push bc + + push bc exx pop hl ; H'L' = Y, X - + ld a, d add a, h ld b, a ; B = y0 + y @@ -466,7 +473,7 @@ __CIRCLE_NEXT: 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 @@ -474,7 +481,7 @@ __CIRCLE_NEXT: 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 @@ -482,7 +489,7 @@ __CIRCLE_NEXT: 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 @@ -490,100 +497,103 @@ __CIRCLE_NEXT: sub l ld c, a ; C = x0 - x call __CIRCLE_PLOT ; plot(x0 - x, y0 - y) - + ld a, d add a, l ld b, a ; B = y0 + x - ld a, e + 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 + 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 + 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 + ld a, e sub h ld c, a ; C = x0 + y call __CIRCLE_PLOT ; plot(x0 - y, y0 - x) - + exx jp __CIRCLE_LOOP - - - + + + __CIRCLE_PLOT: ; Plots a point of the circle, preserving HL and DE push hl push de - call __PLOT + call __PLOT pop de pop hl ret - + ENDP #line 76 "circle.bas" #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -591,64 +601,80 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 77 "circle.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/co.asm b/tests/functional/co.asm new file mode 100644 index 000000000..9e4b187ec --- /dev/null +++ b/tests/functional/co.asm @@ -0,0 +1,35 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/co.bas b/tests/functional/co.bas new file mode 100644 index 000000000..871064241 --- /dev/null +++ b/tests/functional/co.bas @@ -0,0 +1,2 @@ +: + diff --git a/tests/functional/code00.asm b/tests/functional/code00.asm index 921aa86e0..805c41046 100644 --- a/tests/functional/code00.asm +++ b/tests/functional/code00.asm @@ -43,10 +43,12 @@ __CALL_BACK__: __LABEL0: DEFW 0000h #line 1 "load.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -54,25 +56,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -81,50 +83,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -132,12 +135,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -145,7 +149,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -153,9 +157,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -163,25 +168,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -190,39 +195,39 @@ __STOP: ; | (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 + ; 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 @@ -230,57 +235,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -292,39 +297,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -332,15 +337,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -365,14 +370,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -380,24 +385,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "load.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -405,25 +411,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -432,38 +438,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -472,57 +478,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -531,47 +537,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -581,20 +587,22 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "load.asm" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -602,45 +610,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -653,65 +662,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -720,15 +731,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -736,26 +749,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -769,44 +782,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -817,27 +831,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -845,27 +860,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -874,48 +890,50 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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) - -#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -924,32 +942,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -959,7 +977,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -967,19 +985,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -989,7 +1008,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1000,18 +1019,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1021,7 +1041,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1032,18 +1052,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1053,7 +1074,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1066,21 +1087,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1089,79 +1111,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1170,75 +1192,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1248,7 +1270,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1260,16 +1282,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1283,49 +1305,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1336,17 +1358,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1359,27 +1381,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1387,10 +1409,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1407,80 +1429,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1497,8 +1519,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1522,17 +1544,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1543,7 +1565,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1553,21 +1575,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1586,9 +1608,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1613,12 +1635,12 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 4 "load.asm" - + LOAD_CODE: ; This function will implement the LOAD CODE Routine ; Parameters in the stack are HL => String with LOAD name @@ -1626,9 +1648,9 @@ LOAD_CODE: ; 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 @@ -1639,50 +1661,50 @@ LOAD_CODE: LOCAL LD_CH_PR LOCAL LOAD_END LOCAL VR_CONTROL, VR_CONT_1, VR_CONT_2 - + 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 - + LD_BYTES EQU 0556h ; ROM Routine LD-BYTES 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 - + 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 @@ -1693,7 +1715,7 @@ __LOAD_CODE: ; INLINE version ldir pop bc pop hl - + LOAD_HEADER: ex de, hl ; Saves HL in DE ld hl, 10 @@ -1702,46 +1724,46 @@ LOAD_HEADER: 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: ld bc, 17; 2nd Header call __MEM_ALLOC - + ld a, h or l jr nz, LOAD_CONT3; there's memory - + ld a, ERROR_OutOfMemory jp __ERROR - + 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: @@ -1753,22 +1775,22 @@ LD_TYPE: 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 + + 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 + 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 @@ -1776,32 +1798,32 @@ LD_NAME: 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: call __PRINTCHAR ; PRINT-A prints character 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. - + ld a, 0Dh ; prepare carriage return. call __PRINTCHAR ; PRINT-A outputs it. - + 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 @@ -1812,7 +1834,7 @@ VR_CONTROL: ; e.g. LOAD "x" CODE sbc hl, de jr nz, LOAD_ERROR ; Lenghts don't match - + VR_CONT_1: ld hl, HEAD1 + 13 ; fetch start of old data (orig. header) ld a, (hl) @@ -1821,56 +1843,56 @@ VR_CONT_1: ld l, a or h ; check start for zero (unespecified) 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) + 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: pop ix ; Recovers stack frame pointer ld hl, (TMP_HEADER) ; Recovers tmp_header pointer jp MEM_FREE ; Returns via FREE_MEM, freeing tmp header - + ENDP - - + + 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 + ; 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 - + 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: @@ -1879,31 +1901,32 @@ LOOK_NEXT_TAPE_MSG: 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 #line 30 "code00.bas" #line 1 "loadstr.asm" - - + + + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -1912,37 +1935,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 31 "code00.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/code01.asm b/tests/functional/code01.asm index ff67b41b0..0fccfcb58 100644 --- a/tests/functional/code01.asm +++ b/tests/functional/code01.asm @@ -43,10 +43,12 @@ __CALL_BACK__: __LABEL0: DEFW 0000h #line 1 "load.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -54,25 +56,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -81,50 +83,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -132,12 +135,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -145,7 +149,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -153,9 +157,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -163,25 +168,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -190,39 +195,39 @@ __STOP: ; | (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 + ; 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 @@ -230,57 +235,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -292,39 +297,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -332,15 +337,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -365,14 +370,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -380,24 +385,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "load.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -405,25 +411,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -432,38 +438,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -472,57 +478,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -531,47 +537,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -581,20 +587,22 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "load.asm" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -602,45 +610,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -653,65 +662,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -720,15 +731,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -736,26 +749,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -769,44 +782,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -817,27 +831,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -845,27 +860,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -874,48 +890,50 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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) - -#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -924,32 +942,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -959,7 +977,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -967,19 +985,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -989,7 +1008,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1000,18 +1019,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1021,7 +1041,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1032,18 +1052,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1053,7 +1074,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1066,21 +1087,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1089,79 +1111,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1170,75 +1192,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1248,7 +1270,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1260,16 +1282,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1283,49 +1305,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1336,17 +1358,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1359,27 +1381,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1387,10 +1409,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1407,80 +1429,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1497,8 +1519,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1522,17 +1544,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1543,7 +1565,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1553,21 +1575,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1586,9 +1608,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1613,12 +1635,12 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 4 "load.asm" - + LOAD_CODE: ; This function will implement the LOAD CODE Routine ; Parameters in the stack are HL => String with LOAD name @@ -1626,9 +1648,9 @@ LOAD_CODE: ; 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 @@ -1639,50 +1661,50 @@ LOAD_CODE: LOCAL LD_CH_PR LOCAL LOAD_END LOCAL VR_CONTROL, VR_CONT_1, VR_CONT_2 - + 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 - + LD_BYTES EQU 0556h ; ROM Routine LD-BYTES 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 - + 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 @@ -1693,7 +1715,7 @@ __LOAD_CODE: ; INLINE version ldir pop bc pop hl - + LOAD_HEADER: ex de, hl ; Saves HL in DE ld hl, 10 @@ -1702,46 +1724,46 @@ LOAD_HEADER: 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: ld bc, 17; 2nd Header call __MEM_ALLOC - + ld a, h or l jr nz, LOAD_CONT3; there's memory - + ld a, ERROR_OutOfMemory jp __ERROR - + 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: @@ -1753,22 +1775,22 @@ LD_TYPE: 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 + + 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 + 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 @@ -1776,32 +1798,32 @@ LD_NAME: 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: call __PRINTCHAR ; PRINT-A prints character 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. - + ld a, 0Dh ; prepare carriage return. call __PRINTCHAR ; PRINT-A outputs it. - + 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 @@ -1812,7 +1834,7 @@ VR_CONTROL: ; e.g. LOAD "x" CODE sbc hl, de jr nz, LOAD_ERROR ; Lenghts don't match - + VR_CONT_1: ld hl, HEAD1 + 13 ; fetch start of old data (orig. header) ld a, (hl) @@ -1821,56 +1843,56 @@ VR_CONT_1: ld l, a or h ; check start for zero (unespecified) 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) + 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: pop ix ; Recovers stack frame pointer ld hl, (TMP_HEADER) ; Recovers tmp_header pointer jp MEM_FREE ; Returns via FREE_MEM, freeing tmp header - + ENDP - - + + 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 + ; 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 - + 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: @@ -1879,31 +1901,32 @@ LOOK_NEXT_TAPE_MSG: 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 #line 30 "code01.bas" #line 1 "loadstr.asm" - - + + + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -1912,37 +1935,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 31 "code01.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/code02.asm b/tests/functional/code02.asm index f40f7f797..d42f63f8f 100644 --- a/tests/functional/code02.asm +++ b/tests/functional/code02.asm @@ -43,10 +43,12 @@ __CALL_BACK__: __LABEL0: DEFW 0000h #line 1 "load.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -54,25 +56,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -81,50 +83,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -132,12 +135,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -145,7 +149,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -153,9 +157,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -163,25 +168,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -190,39 +195,39 @@ __STOP: ; | (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 + ; 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 @@ -230,57 +235,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -292,39 +297,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -332,15 +337,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -365,14 +370,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -380,24 +385,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "load.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -405,25 +411,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -432,38 +438,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -472,57 +478,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -531,47 +537,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -581,20 +587,22 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "load.asm" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -602,45 +610,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -653,65 +662,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -720,15 +731,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -736,26 +749,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -769,44 +782,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -817,27 +831,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -845,27 +860,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -874,48 +890,50 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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) - -#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -924,32 +942,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -959,7 +977,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -967,19 +985,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -989,7 +1008,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1000,18 +1019,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1021,7 +1041,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1032,18 +1052,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1053,7 +1074,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1066,21 +1087,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1089,79 +1111,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1170,75 +1192,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1248,7 +1270,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1260,16 +1282,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1283,49 +1305,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1336,17 +1358,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1359,27 +1381,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1387,10 +1409,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1407,80 +1429,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1497,8 +1519,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1522,17 +1544,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1543,7 +1565,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1553,21 +1575,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1586,9 +1608,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1613,12 +1635,12 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 4 "load.asm" - + LOAD_CODE: ; This function will implement the LOAD CODE Routine ; Parameters in the stack are HL => String with LOAD name @@ -1626,9 +1648,9 @@ LOAD_CODE: ; 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 @@ -1639,50 +1661,50 @@ LOAD_CODE: LOCAL LD_CH_PR LOCAL LOAD_END LOCAL VR_CONTROL, VR_CONT_1, VR_CONT_2 - + 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 - + LD_BYTES EQU 0556h ; ROM Routine LD-BYTES 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 - + 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 @@ -1693,7 +1715,7 @@ __LOAD_CODE: ; INLINE version ldir pop bc pop hl - + LOAD_HEADER: ex de, hl ; Saves HL in DE ld hl, 10 @@ -1702,46 +1724,46 @@ LOAD_HEADER: 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: ld bc, 17; 2nd Header call __MEM_ALLOC - + ld a, h or l jr nz, LOAD_CONT3; there's memory - + ld a, ERROR_OutOfMemory jp __ERROR - + 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: @@ -1753,22 +1775,22 @@ LD_TYPE: 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 + + 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 + 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 @@ -1776,32 +1798,32 @@ LD_NAME: 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: call __PRINTCHAR ; PRINT-A prints character 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. - + ld a, 0Dh ; prepare carriage return. call __PRINTCHAR ; PRINT-A outputs it. - + 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 @@ -1812,7 +1834,7 @@ VR_CONTROL: ; e.g. LOAD "x" CODE sbc hl, de jr nz, LOAD_ERROR ; Lenghts don't match - + VR_CONT_1: ld hl, HEAD1 + 13 ; fetch start of old data (orig. header) ld a, (hl) @@ -1821,56 +1843,56 @@ VR_CONT_1: ld l, a or h ; check start for zero (unespecified) 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) + 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: pop ix ; Recovers stack frame pointer ld hl, (TMP_HEADER) ; Recovers tmp_header pointer jp MEM_FREE ; Returns via FREE_MEM, freeing tmp header - + ENDP - - + + 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 + ; 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 - + 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: @@ -1879,31 +1901,32 @@ LOOK_NEXT_TAPE_MSG: 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 #line 30 "code02.bas" #line 1 "loadstr.asm" - - + + + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -1912,37 +1935,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 31 "code02.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/codecrash1.asm b/tests/functional/codecrash1.asm index 213fb4a04..bed8b50e4 100644 --- a/tests/functional/codecrash1.asm +++ b/tests/functional/codecrash1.asm @@ -34,11 +34,13 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "asc.asm" + ; Returns the ascii code for the given str #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -46,25 +48,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -73,40 +75,41 @@ __CALL_BACK__: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -114,25 +117,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -141,39 +144,39 @@ __CALL_BACK__: ; | (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 + ; 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 @@ -181,56 +184,56 @@ __CALL_BACK__: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -239,57 +242,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -298,47 +301,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -348,45 +351,45 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "asc.asm" - + __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 #line 22 "codecrash1.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/codecrash2.asm b/tests/functional/codecrash2.asm index f69bf04b8..3114dcf54 100644 --- a/tests/functional/codecrash2.asm +++ b/tests/functional/codecrash2.asm @@ -36,11 +36,13 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "asc.asm" + ; Returns the ascii code for the given str #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -48,25 +50,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -75,40 +77,41 @@ __CALL_BACK__: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -116,25 +119,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -143,39 +146,39 @@ __CALL_BACK__: ; | (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 + ; 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 @@ -183,56 +186,56 @@ __CALL_BACK__: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -241,57 +244,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -300,47 +303,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -350,49 +353,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "asc.asm" - + __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 #line 24 "codecrash2.bas" #line 1 "strcat.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -400,25 +405,25 @@ __ASC_END: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -427,50 +432,51 @@ __ASC_END: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -478,12 +484,13 @@ __ASC_END: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -491,16 +498,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -512,39 +519,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -552,15 +559,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -585,14 +592,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -600,113 +607,114 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "strcat.asm" #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -714,50 +722,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 25 "codecrash2.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/codecrash3.asm b/tests/functional/codecrash3.asm index 7ff4abc2a..e70a29837 100644 --- a/tests/functional/codecrash3.asm +++ b/tests/functional/codecrash3.asm @@ -34,11 +34,13 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "asc.asm" + ; Returns the ascii code for the given str #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -46,25 +48,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -73,40 +75,41 @@ __CALL_BACK__: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -114,25 +117,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -141,39 +144,39 @@ __CALL_BACK__: ; | (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 + ; 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 @@ -181,56 +184,56 @@ __CALL_BACK__: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -239,57 +242,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -298,47 +301,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -348,54 +351,56 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "asc.asm" - + __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 #line 22 "codecrash3.bas" #line 1 "inkey.asm" + ; INKEY Function ; Returns a string allocated in dynamic memory ; containing the string. ; An empty string otherwise. - + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -403,25 +408,25 @@ __ASC_END: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -430,50 +435,51 @@ __ASC_END: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -481,12 +487,13 @@ __ASC_END: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -494,16 +501,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -515,39 +522,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -555,15 +562,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -588,14 +595,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -603,50 +610,50 @@ __MEM_SUBTRACT: 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 - - + + #line 7 "inkey.asm" - + INKEY: - PROC + PROC LOCAL __EMPTY_INKEY LOCAL KEY_SCAN LOCAL KEY_TEST LOCAL KEY_CODE - - ld bc, 3 ; 1 char length string + + 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 @@ -655,7 +662,7 @@ INKEY: dec hl dec hl ; HL Points to string result ret - + __EMPTY_INKEY: pop hl xor a @@ -664,15 +671,15 @@ __EMPTY_INKEY: ld (hl), a dec hl ret - + KEY_SCAN EQU 028Eh KEY_TEST EQU 031Eh KEY_CODE EQU 0333h - + ENDP - + #line 23 "codecrash3.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/codecrash4.asm b/tests/functional/codecrash4.asm index 858ecd965..1778fa842 100644 --- a/tests/functional/codecrash4.asm +++ b/tests/functional/codecrash4.asm @@ -44,11 +44,13 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "asc.asm" + ; Returns the ascii code for the given str #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -56,25 +58,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -83,40 +85,41 @@ __CALL_BACK__: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -124,25 +127,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -151,39 +154,39 @@ __CALL_BACK__: ; | (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 + ; 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 @@ -191,56 +194,56 @@ __CALL_BACK__: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -249,57 +252,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -308,47 +311,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -358,54 +361,56 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "asc.asm" - + __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 #line 32 "codecrash4.bas" #line 1 "inkey.asm" + ; INKEY Function ; Returns a string allocated in dynamic memory ; containing the string. ; An empty string otherwise. - + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -413,25 +418,25 @@ __ASC_END: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -440,50 +445,51 @@ __ASC_END: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -491,12 +497,13 @@ __ASC_END: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -504,16 +511,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -525,39 +532,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -565,15 +572,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -598,14 +605,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -613,50 +620,50 @@ __MEM_SUBTRACT: 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 - - + + #line 7 "inkey.asm" - + INKEY: - PROC + PROC LOCAL __EMPTY_INKEY LOCAL KEY_SCAN LOCAL KEY_TEST LOCAL KEY_CODE - - ld bc, 3 ; 1 char length string + + 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 @@ -665,7 +672,7 @@ INKEY: dec hl dec hl ; HL Points to string result ret - + __EMPTY_INKEY: pop hl xor a @@ -674,109 +681,111 @@ __EMPTY_INKEY: ld (hl), a dec hl ret - + KEY_SCAN EQU 028Eh KEY_TEST EQU 031Eh KEY_CODE EQU 0333h - + ENDP - + #line 33 "codecrash4.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -784,50 +793,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 34 "codecrash4.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/coercion1.asm b/tests/functional/coercion1.asm index 6c19f1b5d..d063e4da8 100644 --- a/tests/functional/coercion1.asm +++ b/tests/functional/coercion1.asm @@ -68,38 +68,40 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -115,7 +117,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -124,43 +126,46 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 59 "coercion1.bas" #line 1 "border.asm" + ; __FASTCALL__ Routine to change de border ; Parameter (color) specified in A register - + BORDER EQU 229Bh - + ; Nothing to do! (Directly from the ZX Spectrum ROM) - + #line 60 "coercion1.bas" #line 1 "divf.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -168,12 +173,13 @@ __ADDF: ; Addition ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -181,34 +187,34 @@ __ERROR: __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 #line 3 "divf.asm" - + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - + __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 @@ -216,26 +222,26 @@ __DIVF: ; Division ld hl, 0 add hl, sp ld (ERR_SP), hl - + ; ------------- 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 @@ -243,55 +249,58 @@ __DIVBYZERO: ld d, a ld e, a ret - + ENDP - + #line 61 "coercion1.bas" #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -299,80 +308,97 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 62 "coercion1.bas" #line 1 "mul8.asm" + __MUL8: ; Performs 8bit x 8bit multiplication PROC - + ;LOCAL __MUL8A LOCAL __MUL8LOOP LOCAL __MUL8B ; 1st operand (byte) in A, 2nd operand into the stack (AF) pop hl ; return address ex (sp), hl ; CALLE convention - + ;;__MUL8_FAST: ; __FASTCALL__ entry ;; ld e, a ;; ld d, 0 ;; ld l, d - ;; - ;; sla h + ;; + ;; sla h ;; jr nc, __MUL8A ;; ld l, e ;; @@ -389,29 +415,30 @@ __MUL8: ; Performs 8bit x 8bit multiplication ;; djnz __MUL8LOOP ;; ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) - + __MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry - + ld b, 8 ld l, a xor a - + __MUL8LOOP: add a, a ; a *= 2 sla l jp nc, __MUL8B add a, h - + __MUL8B: djnz __MUL8LOOP - + ret ; result = HL ENDP - + #line 63 "coercion1.bas" #line 1 "mulf.asm" - - + + + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -420,25 +447,26 @@ __MUL8B: ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __MULF: ; Multiplication call __FPSTACK_PUSH2 - + ; ------------- ROM MUL rst 28h - defb 04h ; + defb 04h ; defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 64 "coercion1.bas" #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -459,10 +487,10 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - + + #line 65 "coercion1.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/coercion3.asm b/tests/functional/coercion3.asm index c438448b8..2cd583dcc 100644 --- a/tests/functional/coercion3.asm +++ b/tests/functional/coercion3.asm @@ -39,11 +39,13 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -51,35 +53,35 @@ __CALL_BACK__: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 6 "copy_attr.asm" - + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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) - -#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -88,39 +90,40 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 30 "coercion3.bas" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -131,16 +134,16 @@ __SET_PAPER2: 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 - + #line 31 "coercion3.bas" - + ZXBASIC_USER_DATA: _c: DEFB 04h diff --git a/tests/functional/const0.asm b/tests/functional/const0.asm index ec7fef5c3..9e4b187ec 100644 --- a/tests/functional/const0.asm +++ b/tests/functional/const0.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/const1.asm b/tests/functional/const1.asm index 8f50ef1b9..c708b31ab 100644 --- a/tests/functional/const1.asm +++ b/tests/functional/const1.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/const3.asm b/tests/functional/const3.asm index c2f386813..834221e3c 100644 --- a/tests/functional/const3.asm +++ b/tests/functional/const3.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _dgConnected: DEFW 0001h diff --git a/tests/functional/const_expr.asm b/tests/functional/const_expr.asm new file mode 100644 index 000000000..2668c3daa --- /dev/null +++ b/tests/functional/const_expr.asm @@ -0,0 +1,133 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 6 + ld (_f), a + ld a, 4 + ld (_f), a + ld a, 5 + ld (_f), a + ld a, 5 + ld (_f), a + ld a, 6 + ld (_f), a + ld a, 252 + ld (_f), a + ld a, 5 + ld (_f), a + xor a + ld (_f), a + ld a, 2 + ld (_f), a + xor a + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 5 + ld (_f), a + ld a, 53 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 5 + ld (_f), a + ld a, 5 + ld (_f), a + ld a, 5 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 5 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + xor a + ld (_f), a + xor a + ld (_f), a + ld a, 1 + ld (_f), a + xor a + ld (_f), a + xor a + ld (_f), a + ld a, 1 + ld (_f), a + xor a + ld (_f), a + xor a + ld (_f), a + ld a, 1 + ld (_f), a + xor a + ld (_f), a + xor a + ld (_f), a + ld a, 1 + ld (_f), a + xor a + ld (_f), a + xor a + ld (_f), a + ld a, 1 + ld (_f), a + xor a + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + ld a, 1 + ld (_f), a + xor a + ld (_f), a + ld a, 1 + ld (_f), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_f: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/const_expr.bas b/tests/functional/const_expr.bas new file mode 100644 index 000000000..6c4dccbac --- /dev/null +++ b/tests/functional/const_expr.bas @@ -0,0 +1,63 @@ + +const g as Uinteger = 1 + +f = 5 + g +f = 5 - g +f = 5 * g +f = 5 / g + +f = g + 5 +f = g - 5 +f = g * 5 +f = g / 5 + +f = g + g +f = g - g +f = g * g +f = g / g + +f = g ^ 5 +f = 5 ^ g +f = 5 ^ 5 +f = g ^ g + +f = 5 | 5 +f = 5 | g +f = g | 5 +f = g | g + +f = 5 & 5 +f = g & 5 +f = 5 & g +f = g & g + +f = 5 mod 2 +f = g mod 5 +f = 5 mod g +f = g mod g + +f = (5 = 5) +f = (5 = g) +f = (g = 5) +f = (g = g) + +f = (5 < 5) +f = (5 < g) +f = (g < 5) +f = (g < g) + +f = (5 > 5) +f = (5 > g) +f = (g > 5) +f = (g > g) + +f = (5 <= 5) +f = (5 <= g) +f = (g <= 5) +f = (g <= g) + +f = (5 >= 5) +f = (5 >= g) +f = (g >= 5) +f = (g >= g) + diff --git a/tests/functional/constrig.asm b/tests/functional/constrig.asm new file mode 100644 index 000000000..d1e9be1de --- /dev/null +++ b/tests/functional/constrig.asm @@ -0,0 +1,113 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 07Fh + ld de, 099B3h + ld bc, 0F5DBh + ld hl, _a + call __STOREF + ld a, 080h + ld de, 0BBEFh + ld bc, 01EA0h + ld hl, _a + call __STOREF + ld a, 07Fh + ld de, 0C93Fh + ld bc, 064B0h + ld hl, _a + call __STOREF + ld a, 07Dh + ld de, 0244Dh + ld bc, 0B093h + ld hl, _a + call __STOREF + ld a, 081h + ld de, 03D3Ch + ld bc, 06791h + ld hl, _a + call __STOREF + ld a, 07Fh + ld de, 03915h + ld bc, 030D3h + ld hl, _a + call __STOREF + ld a, 081h + ld de, 05A20h + ld bc, 07589h + ld hl, _a + call __STOREF + ld a, 086h + ld de, 07604h + ld bc, 00939h + ld hl, _a + call __STOREF + ld a, 081h + ld de, 0776Fh + ld bc, 08B50h + ld hl, _a + call __STOREF + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "storef.asm" + +__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 + +#line 63 "constrig.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00, 00, 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/constrig.bas b/tests/functional/constrig.bas new file mode 100644 index 000000000..d66e88a2f --- /dev/null +++ b/tests/functional/constrig.bas @@ -0,0 +1,13 @@ + +DIM a as Float + +a = SIN(3.5) +a = COS(3.5) +a = TAN(3.5) +a = ASN(0.1) +a = ACS(0.1) +a = ATN(0.3) +a = LN(3.5) +a = EXP(3.5) +a = SQR(3.5) + diff --git a/tests/functional/cpeq16.asm b/tests/functional/cpeq16.asm index 5a54c2ef9..40d803a43 100644 --- a/tests/functional/cpeq16.asm +++ b/tests/functional/cpeq16.asm @@ -37,6 +37,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "eq16.asm" + __EQ16: ; Test if 16bit values HL == DE ; Returns result in A: 0 = False, FF = True xor a ; Reset carry flag @@ -44,9 +45,9 @@ __EQ16: ; Test if 16bit values HL == DE ret nz inc a ret - + #line 28 "cpeq16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/data0.bas b/tests/functional/data0.bas new file mode 100644 index 000000000..b6286f25b --- /dev/null +++ b/tests/functional/data0.bas @@ -0,0 +1,3 @@ +DIM a as Byte +DATA a, b(2) + diff --git a/tests/functional/dbbad.asm b/tests/functional/dbbad.asm new file mode 100644 index 000000000..f65f5b5bb --- /dev/null +++ b/tests/functional/dbbad.asm @@ -0,0 +1,4 @@ + + +db 256 + diff --git a/tests/functional/dbbad.bin b/tests/functional/dbbad.bin new file mode 100644 index 000000000..f76dd238a Binary files /dev/null and b/tests/functional/dbbad.bin differ diff --git a/tests/functional/declare0.asm b/tests/functional/declare0.asm index ec7fef5c3..9e4b187ec 100644 --- a/tests/functional/declare0.asm +++ b/tests/functional/declare0.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/def_func_inline.bas b/tests/functional/def_func_inline.bas new file mode 100644 index 000000000..f5d6fa0c2 --- /dev/null +++ b/tests/functional/def_func_inline.bas @@ -0,0 +1,3 @@ + +FUNCTION f() END FUNCTION + diff --git a/tests/functional/def_func_inline_for_next.asm b/tests/functional/def_func_inline_for_next.asm new file mode 100644 index 000000000..5d2c75363 --- /dev/null +++ b/tests/functional/def_func_inline_for_next.asm @@ -0,0 +1,59 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_f: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + ld (ix-1), 1 + jp __LABEL0 +__LABEL3: +__LABEL4: + inc (ix-1) +__LABEL0: + ld a, (ix-1) + push af + ld a, 5 + pop hl + cp h + jp nc, __LABEL3 +__LABEL2: +_f__leave: + ld sp, ix + pop ix + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/def_func_inline_for_next.bas b/tests/functional/def_func_inline_for_next.bas new file mode 100644 index 000000000..c0e763cc8 --- /dev/null +++ b/tests/functional/def_func_inline_for_next.bas @@ -0,0 +1,3 @@ + +FUNCTION f(): FOR i = 1 TO 5: NEXT: END FUNCTION + diff --git a/tests/functional/def_func_inline_ok.asm b/tests/functional/def_func_inline_ok.asm new file mode 100644 index 000000000..c6df31a38 --- /dev/null +++ b/tests/functional/def_func_inline_ok.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_f: + push ix + ld ix, 0 + add ix, sp +_f__leave: + ld sp, ix + pop ix + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/def_func_inline_ok.bas b/tests/functional/def_func_inline_ok.bas new file mode 100644 index 000000000..d711fd3d6 --- /dev/null +++ b/tests/functional/def_func_inline_ok.bas @@ -0,0 +1,3 @@ + +FUNCTION f(): END FUNCTION + diff --git a/tests/functional/defb.asm b/tests/functional/defb.asm index 12c58809a..7fd673aa7 100644 --- a/tests/functional/defb.asm +++ b/tests/functional/defb.asm @@ -29,7 +29,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/dim_const0.asm b/tests/functional/dim_const0.asm new file mode 100644 index 000000000..7277713b2 --- /dev/null +++ b/tests/functional/dim_const0.asm @@ -0,0 +1,40 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_c: + DEFW 0000h + DEFB 01h + DEFB 7 + DEFB 7 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/dim_const0.bas b/tests/functional/dim_const0.bas new file mode 100644 index 000000000..56a40d49d --- /dev/null +++ b/tests/functional/dim_const0.bas @@ -0,0 +1,3 @@ +CONST xx as UByte = 7 +DIM c(1) as Ubyte = {xx, xx} + diff --git a/tests/functional/dimconst.asm b/tests/functional/dimconst.asm index 5ae6abc2f..0ee746293 100644 --- a/tests/functional/dimconst.asm +++ b/tests/functional/dimconst.asm @@ -44,7 +44,7 @@ _p__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _Map: DEFB 00, 00 diff --git a/tests/functional/dimconst2.asm b/tests/functional/dimconst2.asm index d9eac9d13..6cd67a27a 100644 --- a/tests/functional/dimconst2.asm +++ b/tests/functional/dimconst2.asm @@ -45,7 +45,7 @@ _q__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFW __LABEL__Map diff --git a/tests/functional/dimconst2c.asm b/tests/functional/dimconst2c.asm index 310285840..c43c9d1ac 100644 --- a/tests/functional/dimconst2c.asm +++ b/tests/functional/dimconst2c.asm @@ -46,7 +46,7 @@ _q__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _Map: DEFB 00 diff --git a/tests/functional/dimconst2d.asm b/tests/functional/dimconst2d.asm index 35a6bfd0a..a981f1038 100644 --- a/tests/functional/dimconst2d.asm +++ b/tests/functional/dimconst2d.asm @@ -44,7 +44,7 @@ _q__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _Map: DEFB 01h diff --git a/tests/functional/dimconst2e.asm b/tests/functional/dimconst2e.asm index e1c5f1ea6..2303f7bb1 100644 --- a/tests/functional/dimconst2e.asm +++ b/tests/functional/dimconst2e.asm @@ -48,7 +48,7 @@ _q__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _Map: DEFB 00 diff --git a/tests/functional/dimconst3.asm b/tests/functional/dimconst3.asm index bf384a91d..b542ff3f8 100644 --- a/tests/functional/dimconst3.asm +++ b/tests/functional/dimconst3.asm @@ -27,7 +27,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW __LABEL__Map diff --git a/tests/functional/dimconst4.asm b/tests/functional/dimconst4.asm index 81a7d79c0..9f4e0db87 100644 --- a/tests/functional/dimconst4.asm +++ b/tests/functional/dimconst4.asm @@ -27,7 +27,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB (__LABEL__Map) & 0xFF diff --git a/tests/functional/dimconst4b.asm b/tests/functional/dimconst4b.asm index bf384a91d..b542ff3f8 100644 --- a/tests/functional/dimconst4b.asm +++ b/tests/functional/dimconst4b.asm @@ -27,7 +27,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW __LABEL__Map diff --git a/tests/functional/dimconst4c.asm b/tests/functional/dimconst4c.asm index 8ee729da7..801ce20bd 100644 --- a/tests/functional/dimconst4c.asm +++ b/tests/functional/dimconst4c.asm @@ -27,7 +27,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFW ((__LABEL__Map) & 0xFFFFFFFF) & 0xFFFF diff --git a/tests/functional/dimconst5.asm b/tests/functional/dimconst5.asm index 99c83eb1c..5902a6cd6 100644 --- a/tests/functional/dimconst5.asm +++ b/tests/functional/dimconst5.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 0FEh diff --git a/tests/functional/dimconst6.asm b/tests/functional/dimconst6.asm index 00fcfa54a..9dbc122e7 100644 --- a/tests/functional/dimconst6.asm +++ b/tests/functional/dimconst6.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _Map: DEFB 34h diff --git a/tests/functional/div32.asm b/tests/functional/div32.asm index 18f1012da..e5877898f 100644 --- a/tests/functional/div32.asm +++ b/tests/functional/div32.asm @@ -37,38 +37,41 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "negf.asm" + #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -77,35 +80,35 @@ __I8TOFREG: 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 @@ -113,20 +116,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -134,45 +137,47 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 2 "negf.asm" #line 1 "ftou32reg.asm" - - + + + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -180,95 +185,112 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 3 "negf.asm" #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -284,30 +306,30 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 4 "negf.asm" - + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - -__NEGF: ; A = -A + +__NEGF: ; A = -A call __FPSTACK_PUSH - + ; ------------- ROM NEGATE rst 28h defb 1Bh ; NEGF defb 38h; ; END CALC - + jp __FPSTACK_POP - - + + #line 28 "div32.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/divf00.asm b/tests/functional/divf00.asm index e74a53fad..16e912b89 100644 --- a/tests/functional/divf00.asm +++ b/tests/functional/divf00.asm @@ -35,38 +35,40 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "divf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -83,19 +85,20 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK jp __FPSTACK_PUSH #line 2 "divf.asm" #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -103,12 +106,13 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -116,34 +120,34 @@ __ERROR: __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 #line 3 "divf.asm" - + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - + __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 @@ -151,26 +155,26 @@ __DIVF: ; Division ld hl, 0 add hl, sp ld (ERR_SP), hl - + ; ------------- 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 @@ -178,17 +182,18 @@ __DIVBYZERO: ld d, a ld e, a ret - + ENDP - + #line 26 "divf00.bas" #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -209,10 +214,11 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - + + #line 27 "divf00.bas" #line 1 "storef.asm" + __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 @@ -220,7 +226,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -228,7 +234,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -240,9 +246,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 28 "divf00.bas" - + ZXBASIC_USER_DATA: _a: DEFB 80h diff --git a/tests/functional/divf01.asm b/tests/functional/divf01.asm index b2ddca57e..f65528a7b 100644 --- a/tests/functional/divf01.asm +++ b/tests/functional/divf01.asm @@ -39,38 +39,40 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "divf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -87,19 +89,20 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK jp __FPSTACK_PUSH #line 2 "divf.asm" #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -107,12 +110,13 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -120,34 +124,34 @@ __ERROR: __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 #line 3 "divf.asm" - + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - + __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 @@ -155,26 +159,26 @@ __DIVF: ; Division ld hl, 0 add hl, sp ld (ERR_SP), hl - + ; ------------- 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 @@ -182,11 +186,12 @@ __DIVBYZERO: ld d, a ld e, a ret - + ENDP - + #line 30 "divf01.bas" #line 1 "storef.asm" + __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 @@ -194,7 +199,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -202,7 +207,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -214,9 +219,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 31 "divf01.bas" - + ZXBASIC_USER_DATA: _a: DEFB 80h diff --git a/tests/functional/divf16.asm b/tests/functional/divf16.asm index a39f3bdca..047aba3d5 100644 --- a/tests/functional/divf16.asm +++ b/tests/functional/divf16.asm @@ -94,40 +94,43 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "divf16.asm" + #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -139,7 +142,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -155,9 +158,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -165,29 +168,29 @@ __DIV32LOOP: 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 @@ -197,34 +200,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -232,126 +235,126 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 2 "divf16.asm" - - -__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 - + + +__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 + #line 85 "divf16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/divf16a.asm b/tests/functional/divf16a.asm index 3abdba819..64324b919 100644 --- a/tests/functional/divf16a.asm +++ b/tests/functional/divf16a.asm @@ -41,40 +41,43 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "divf16.asm" + #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -86,7 +89,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -102,9 +105,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -112,29 +115,29 @@ __DIV32LOOP: 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 @@ -144,34 +147,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -179,126 +182,126 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 2 "divf16.asm" - - -__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 - + + +__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 + #line 32 "divf16a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/divf16b.asm b/tests/functional/divf16b.asm index 8a8edaeae..ee68c738c 100644 --- a/tests/functional/divf16b.asm +++ b/tests/functional/divf16b.asm @@ -51,40 +51,43 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "divf16.asm" + #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -96,7 +99,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -112,9 +115,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -122,29 +125,29 @@ __DIV32LOOP: 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 @@ -154,34 +157,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -189,126 +192,126 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 2 "divf16.asm" - - -__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 - + + +__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 + #line 42 "divf16b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/divf16c.asm b/tests/functional/divf16c.asm index 4446fa316..fdd24169d 100644 --- a/tests/functional/divf16c.asm +++ b/tests/functional/divf16c.asm @@ -77,40 +77,43 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "divf16.asm" + #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -122,7 +125,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -138,9 +141,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -148,29 +151,29 @@ __DIV32LOOP: 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 @@ -180,34 +183,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -215,144 +218,145 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 2 "divf16.asm" - - -__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 - + + +__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 + #line 68 "divf16c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 69 "divf16c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/divi16a.asm b/tests/functional/divi16a.asm index fd30e94ef..7fd7359f6 100644 --- a/tests/functional/divi16a.asm +++ b/tests/functional/divi16a.asm @@ -33,15 +33,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -51,23 +53,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -76,46 +78,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -123,35 +125,35 @@ __DIVI16_FAST: 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 + + 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 - + #line 24 "divi16a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/divi16b.asm b/tests/functional/divi16b.asm index 6c545a2ea..1098b4d3e 100644 --- a/tests/functional/divi16b.asm +++ b/tests/functional/divi16b.asm @@ -40,15 +40,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -58,23 +60,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -83,46 +85,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -130,35 +132,35 @@ __DIVI16_FAST: 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 + + 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 - + #line 31 "divi16b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/divi32c.asm b/tests/functional/divi32c.asm index 7a4148dbc..4ec3c1599 100644 --- a/tests/functional/divi32c.asm +++ b/tests/functional/divi32c.asm @@ -80,39 +80,41 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -124,7 +126,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -140,9 +142,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -150,29 +152,29 @@ __DIV32LOOP: 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 @@ -182,34 +184,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -217,61 +219,62 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 71 "divi32c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 72 "divi32c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/divi8.asm b/tests/functional/divi8.asm index 63aae2858..5d99b6b6e 100644 --- a/tests/functional/divi8.asm +++ b/tests/functional/divi8.asm @@ -54,49 +54,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -104,45 +105,45 @@ __DIV8A: 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 - + 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 - + #line 45 "divi8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/divi8a.asm b/tests/functional/divi8a.asm index 195febb06..508914121 100644 --- a/tests/functional/divi8a.asm +++ b/tests/functional/divi8a.asm @@ -33,49 +33,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -83,45 +84,45 @@ __DIV8A: 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 - + 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 - + #line 24 "divi8a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/divi8b.asm b/tests/functional/divi8b.asm index eda5776d2..48e4bf660 100644 --- a/tests/functional/divi8b.asm +++ b/tests/functional/divi8b.asm @@ -39,49 +39,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -89,45 +90,45 @@ __DIV8A: 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 - + 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 - + #line 30 "divi8b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/divu16.asm b/tests/functional/divu16.asm index 39dd306d1..38807632a 100644 --- a/tests/functional/divu16.asm +++ b/tests/functional/divu16.asm @@ -55,15 +55,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -73,23 +75,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -98,46 +100,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -145,35 +147,35 @@ __DIVI16_FAST: 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 + + 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 - + #line 46 "divu16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/divu16a.asm b/tests/functional/divu16a.asm index d8437c72f..1e418a809 100644 --- a/tests/functional/divu16a.asm +++ b/tests/functional/divu16a.asm @@ -33,15 +33,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -51,23 +53,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -76,46 +78,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -123,35 +125,35 @@ __DIVI16_FAST: 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 + + 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 - + #line 24 "divu16a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/divu16b.asm b/tests/functional/divu16b.asm index 7ea2cd779..fe63f4a4a 100644 --- a/tests/functional/divu16b.asm +++ b/tests/functional/divu16b.asm @@ -40,15 +40,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -58,23 +60,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -83,46 +85,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -130,35 +132,35 @@ __DIVI16_FAST: 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 + + 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 - + #line 31 "divu16b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/divu32c.asm b/tests/functional/divu32c.asm index ca80e41c5..7403f96be 100644 --- a/tests/functional/divu32c.asm +++ b/tests/functional/divu32c.asm @@ -80,39 +80,41 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -124,7 +126,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -140,9 +142,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -150,29 +152,29 @@ __DIV32LOOP: 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 @@ -182,34 +184,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -217,61 +219,62 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 71 "divu32c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 72 "divu32c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/divu8.asm b/tests/functional/divu8.asm index 15d4ca27c..bd523e67d 100644 --- a/tests/functional/divu8.asm +++ b/tests/functional/divu8.asm @@ -54,49 +54,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -104,45 +105,45 @@ __DIV8A: 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 - + 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 - + #line 45 "divu8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/divu8a.asm b/tests/functional/divu8a.asm index 49c5746fb..c81ed5912 100644 --- a/tests/functional/divu8a.asm +++ b/tests/functional/divu8a.asm @@ -33,49 +33,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -83,45 +84,45 @@ __DIV8A: 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 - + 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 - + #line 24 "divu8a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/divu8b.asm b/tests/functional/divu8b.asm index a0afff594..f98ec2063 100644 --- a/tests/functional/divu8b.asm +++ b/tests/functional/divu8b.asm @@ -39,49 +39,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -89,45 +90,45 @@ __DIV8A: 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 - + 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 - + #line 30 "divu8b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/dollar.asm b/tests/functional/dollar.asm index 18b63bae6..bceba2626 100644 --- a/tests/functional/dollar.asm +++ b/tests/functional/dollar.asm @@ -29,7 +29,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/doloop.asm b/tests/functional/doloop.asm index df3d8d289..39cac0be1 100644 --- a/tests/functional/doloop.asm +++ b/tests/functional/doloop.asm @@ -21,16 +21,14 @@ __LABEL3: __LABEL__30: __LABEL4: __LABEL__40: - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL__50: jp __LABEL4 __LABEL5: __LABEL6: - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) jp __LABEL6 __LABEL7: ld hl, 0 @@ -49,7 +47,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/doloop1.asm b/tests/functional/doloop1.asm index 93885068f..b7f6f3e08 100644 --- a/tests/functional/doloop1.asm +++ b/tests/functional/doloop1.asm @@ -29,7 +29,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/doloop2.asm b/tests/functional/doloop2.asm index f6fff4bc5..632da5bec 100644 --- a/tests/functional/doloop2.asm +++ b/tests/functional/doloop2.asm @@ -45,7 +45,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/doloop3.asm b/tests/functional/doloop3.asm index a6a75e09b..4473b0580 100644 --- a/tests/functional/doloop3.asm +++ b/tests/functional/doloop3.asm @@ -46,7 +46,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/doloop4.asm b/tests/functional/doloop4.asm new file mode 100644 index 000000000..dd15f3a3b --- /dev/null +++ b/tests/functional/doloop4.asm @@ -0,0 +1,38 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__10: +__LABEL0: +__LABEL__20: + jp __LABEL0 +__LABEL1: + jp __LABEL__20 +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/doloop4.bas b/tests/functional/doloop4.bas new file mode 100644 index 000000000..f0bf9f652 --- /dev/null +++ b/tests/functional/doloop4.bas @@ -0,0 +1,8 @@ +REM DO..LOOP syntax test + + +10 DO +20 LOOP + +GOTO 20 + diff --git a/tests/functional/doloopuntilsplitted.bas b/tests/functional/doloopuntilsplitted.bas new file mode 100644 index 000000000..824b12214 --- /dev/null +++ b/tests/functional/doloopuntilsplitted.bas @@ -0,0 +1,6 @@ + +DO +LET M=0: LOOP UNTIL i=1 + +DO LET M=0: LOOP UNTIL i=1 + diff --git a/tests/functional/dountil1.asm b/tests/functional/dountil1.asm index ef2592285..20fc6c5c2 100644 --- a/tests/functional/dountil1.asm +++ b/tests/functional/dountil1.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/dountilempty.asm b/tests/functional/dountilempty.asm new file mode 100644 index 000000000..af6f7511f --- /dev/null +++ b/tests/functional/dountilempty.asm @@ -0,0 +1,73 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + jp __LABEL2 +__LABEL0: +__LABEL2: + ld a, 10 + ld hl, (_i - 1) + call __LTI8 + or a + jp z, __LABEL0 +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 27 "dountilempty.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/dountilempty.bas b/tests/functional/dountilempty.bas new file mode 100644 index 000000000..14b3b891a --- /dev/null +++ b/tests/functional/dountilempty.bas @@ -0,0 +1,4 @@ + +DIM i as Byte +DO UNTIL i > 10 LOOP + diff --git a/tests/functional/dountilsplitted.asm b/tests/functional/dountilsplitted.asm new file mode 100644 index 000000000..cc2091024 --- /dev/null +++ b/tests/functional/dountilsplitted.asm @@ -0,0 +1,393 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + jp __LABEL2 +__LABEL0: + xor a + ld (_M), a +__LABEL2: + ld hl, _i + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __EQF + or a + jp z, __LABEL0 +__LABEL1: + jp __LABEL5 +__LABEL3: + xor a + ld (_M), a +__LABEL5: + ld hl, _i + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __EQF + or a + jp z, __LABEL3 +__LABEL4: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "eqf.asm" + +#line 1 "u32tofreg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "u32tofreg.asm" +__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 + +#line 2 "eqf.asm" +#line 1 "ftou32reg.asm" + + + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 3 "eqf.asm" +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 4 "eqf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + + +__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 + +#line 46 "dountilsplitted.bas" +#line 1 "pushf.asm" + + + ; 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 + +__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 + + +#line 47 "dountilsplitted.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00, 00, 00, 00, 00 +_M: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/dountilsplitted.bas b/tests/functional/dountilsplitted.bas new file mode 100644 index 000000000..fae94b004 --- /dev/null +++ b/tests/functional/dountilsplitted.bas @@ -0,0 +1,6 @@ + +DO UNTIL i=1: +LET M=0: LOOP + +DO UNTIL i=1: LET M=0: LOOP + diff --git a/tests/functional/dowhile1.asm b/tests/functional/dowhile1.asm index ef2592285..20fc6c5c2 100644 --- a/tests/functional/dowhile1.asm +++ b/tests/functional/dowhile1.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/dowhileempty.asm b/tests/functional/dowhileempty.asm new file mode 100644 index 000000000..ef8d37c25 --- /dev/null +++ b/tests/functional/dowhileempty.asm @@ -0,0 +1,73 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + jp __LABEL2 +__LABEL0: +__LABEL2: + ld h, 10 + ld a, (_i) + call __LTI8 + or a + jp nz, __LABEL0 +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 27 "dowhileempty.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/dowhileempty.bas b/tests/functional/dowhileempty.bas new file mode 100644 index 000000000..305c84849 --- /dev/null +++ b/tests/functional/dowhileempty.bas @@ -0,0 +1,5 @@ + +DIM i as Byte + +DO WHILE i < 10 LOOP + diff --git a/tests/functional/dowhilesplitted.asm b/tests/functional/dowhilesplitted.asm new file mode 100644 index 000000000..3735e3e46 --- /dev/null +++ b/tests/functional/dowhilesplitted.asm @@ -0,0 +1,393 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + jp __LABEL2 +__LABEL0: + xor a + ld (_M), a +__LABEL2: + ld hl, _i + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __EQF + or a + jp nz, __LABEL0 +__LABEL1: + jp __LABEL5 +__LABEL3: + xor a + ld (_M), a +__LABEL5: + ld hl, _i + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __EQF + or a + jp nz, __LABEL3 +__LABEL4: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "eqf.asm" + +#line 1 "u32tofreg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "u32tofreg.asm" +__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 + +#line 2 "eqf.asm" +#line 1 "ftou32reg.asm" + + + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 3 "eqf.asm" +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 4 "eqf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + + +__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 + +#line 46 "dowhilesplitted.bas" +#line 1 "pushf.asm" + + + ; 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 + +__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 + + +#line 47 "dowhilesplitted.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00, 00, 00, 00, 00 +_M: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/dowhilesplitted.bas b/tests/functional/dowhilesplitted.bas new file mode 100644 index 000000000..d14789f40 --- /dev/null +++ b/tests/functional/dowhilesplitted.bas @@ -0,0 +1,6 @@ + +DO WHILE i=1: +LET M=0: LOOP + +DO WHILE i=1: LET M=0: LOOP + diff --git a/tests/functional/draw.asm b/tests/functional/draw.asm index 5923eedc0..351e83dfb 100644 --- a/tests/functional/draw.asm +++ b/tests/functional/draw.asm @@ -55,27 +55,29 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "draw.asm" + ; 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 - + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -83,12 +85,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -96,7 +99,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -104,11 +107,13 @@ __STOP: ret #line 9 "draw.asm" #line 1 "in_screen.asm" + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -116,75 +121,76 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 2 "in_screen.asm" - - + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 10 "draw.asm" - + #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -197,184 +203,188 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 12 "draw.asm" - + #line 1 "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 - -SP.PixelDown: - inc h - ld a,h - and $07 - ret nz - ex af, af' ; Sets carry on F' - scf ; 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 - ret nc - ld a,h - add a,$08 - ld h,a - ;IF DISP_HIRES - ; and $18 - ; cp $18 - ;ELSE - cp $58 - ;ENDIF - ccf - ret + + ; + ; 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 + +SP.PixelDown: + inc h + ld a,h + and $07 + ret nz + ex af, af' ; Sets carry on F' + scf ; 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 + ret nc + ld a,h + add a,$08 + ld h,a + ;IF DISP_HIRES + ; and $18 + ; cp $18 + ;ELSE + cp $58 + ;ENDIF + ccf + ret #line 14 "draw.asm" #line 1 "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 - -SP.PixelUp: - ld a,h - dec h - and $07 - ret nz - ex af, af' - scf - ex af, af' - ld a,$08 - add a,h - ld h,a - ld a,l - sub $20 - ld l,a - ret nc - ld a,h - sub $08 - ld h,a - ;IF DISP_HIRES - ; and $18 - ; cp $18 - ; ccf - ;ELSE - cp $40 - ;ENDIF - ret + + ; + ; 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 + +SP.PixelUp: + ld a,h + dec h + and $07 + ret nz + ex af, af' + scf + ex af, af' + ld a,$08 + add a,h + ld h,a + ld a,l + sub $20 + ld l,a + ret nc + ld a,h + sub $08 + ld h,a + ;IF DISP_HIRES + ; and $18 + ; cp $18 + ; ccf + ;ELSE + cp $40 + ;ENDIF + ret #line 15 "draw.asm" #line 1 "PixelLeft.asm" - ; - ; 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 - - -SP.PixelLeft: - rlca ; Sets new pixel bit 1 to the right - ret nc - 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 - ret - + + ; + ; 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 + + +SP.PixelLeft: + rlca ; Sets new pixel bit 1 to the right + ret nc + 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 + ret + #line 16 "draw.asm" #line 1 "PixelRight.asm" - ; - ; 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 - - -SP.PixelRight: - rrca ; Sets new pixel bit 1 to the right - ret nc - 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 - ret - + + ; + ; 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 + + +SP.PixelRight: + rrca ; Sets new pixel bit 1 to the right + ret nc + 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 + ret + #line 17 "draw.asm" - + ;; DRAW PROCEDURE - PROC - + PROC + LOCAL __DRAW1 LOCAL __DRAW2 LOCAL __DRAW3 @@ -386,22 +396,22 @@ SP.PixelRight: LOCAL __INCX, __INCY, __DECX, __DECY LOCAL P_FLAG P_FLAG EQU 23697 - + __DRAW_ERROR: jp __OUT_OF_SCREEN_ERR - + DRAW: ;; ENTRY POINT - + LOCAL PIXEL_ADDR 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 @@ -409,26 +419,26 @@ DRAW: 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 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 @@ -437,7 +447,7 @@ __DRAW: ld (__PLOTINVERSE), a ld a, 0A6h ; and (hl) jp __DRAW_START - + __DRAW_SETUP1: xor a ; nop ld (__PLOTINVERSE), a @@ -445,7 +455,7 @@ __DRAW_SETUP1: bit 0, c ; Test for OVER jr z, __DRAW_START ld a, 0AEh ; xor (hl) - + __DRAW_START: ld (__PLOTOVER), a ; "Pokes" last operation exx @@ -455,7 +465,7 @@ __DRAW_START: LOCAL __PIXEL_ADDR __PIXEL_ADDR EQU 22ACh call __PIXEL_ADDR - + ;; Now gets pixel mask in A register ld b, a inc b @@ -465,7 +475,7 @@ __DRAW_START: __PIXEL_MASK: rra djnz __PIXEL_MASK - + ld b, d ; Restores B' from D' pop de ; D'E' = y2, x2 exx ; At this point: D'E' = y2,x2 coords @@ -473,147 +483,147 @@ __PIXEL_MASK: ex af, af' ; Saves A reg for later ; A' = Pixel mask ; H'L' = Screen Address of pixel - + ld bc, (COORDS) ; B,C = y1, x1 - - ld a, e + + 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 - + jr z, __DRAW4 ; if error < 0 + exx - add hl, bc ; error += dY + 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 dyncamically updated ld a, e ; Restores A reg. call __FASTPLOT - + __DRAW4_LOOP: ld a, b cp d jp nz, __DRAW3 ld (COORDS), bc - ret - + ret + __DRAW_DX_GT_DY: ; DX > DY ; -------------------------- ; HL = error = dX / 2 ld h, 0 - ld l, c + 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 ; + jr z, __DRAW6 ; exx add hl, bc ; error += dX - exx - + exx + DY2: ; y += yi inc b call __INCY ; This address will be dynamically updated - + __DRAW6: ld a, e DX2: ; x += xi @@ -621,40 +631,40 @@ DX2: ; x += xi 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 - - PIXEL_ADDR EQU 22ACh + + PIXEL_ADDR EQU 22ACh 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: @@ -662,12 +672,12 @@ __PLOTINVERSE: __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 ;; gets ATTR position with offset given in SCREEN_ADDR @@ -680,69 +690,72 @@ __PLOTOVER: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR call PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + pop de pop hl - - LOCAL __FASTPLOTEND -__FASTPLOTEND: + + LOCAL __FASTPLOTEND +__FASTPLOTEND: or a ; Resets carry flag ex af, af' ; Recovers A reg ld a, e ret - + ENDP - + #line 46 "draw.bas" #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -750,64 +763,80 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 47 "draw.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/draw3.asm b/tests/functional/draw3.asm index 66fbfff69..d7ad6f774 100644 --- a/tests/functional/draw3.asm +++ b/tests/functional/draw3.asm @@ -85,29 +85,31 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "draw3.asm" + ; ----------------------------------------------------------- -; vim: et:ts=4:sw=4:ruler: +; vim: et:ts=4:sw=4:ruler: ; ; DRAW an arc using ZX ROM algorithm. ; DRAW x, y, r => r = Arc in radians - + ; r parameter in A ED BC register ; X, and Y parameter in high byte on top of the stack - + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -115,12 +117,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -128,7 +131,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -136,19 +139,22 @@ __STOP: ret #line 11 "draw3.asm" #line 1 "plot.asm" + ; 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 - - + + #line 1 "in_screen.asm" + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -156,74 +162,75 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 2 "in_screen.asm" - - + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 9 "plot.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -236,48 +243,48 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 10 "plot.asm" - + 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 - + ld c, h + 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 @@ -285,7 +292,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) res 6, h ; Starts from 0 ld bc, (SCREEN_ADDR) add hl, bc ; Now current offset - + ld b, a inc b ld a, 0FEh @@ -293,7 +300,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) __PLOT_LOOP: rrca djnz __PLOT_LOOP - + ld b, a ld a, (P_FLAG) ld c, a @@ -301,18 +308,18 @@ __PLOT_LOOP: 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 - + ;; gets ATTR position with offset given in SCREEN_ADDR ld a, h rrca @@ -323,51 +330,52 @@ __PLOT_END: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR jp PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + __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 + PIXEL_ADDR EQU 22ACh COORDS EQU 5C7Dh ENDP #line 12 "draw3.asm" #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -384,176 +392,181 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK jp __FPSTACK_PUSH #line 13 "draw3.asm" #line 1 "draw.asm" + ; 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 - - - - - - + + + + + + #line 1 "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 - -SP.PixelDown: - inc h - ld a,h - and $07 - ret nz - ex af, af' ; Sets carry on F' - scf ; 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 - ret nc - ld a,h - add a,$08 - ld h,a - ;IF DISP_HIRES - ; and $18 - ; cp $18 - ;ELSE - cp $58 - ;ENDIF - ccf - ret + + ; + ; 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 + +SP.PixelDown: + inc h + ld a,h + and $07 + ret nz + ex af, af' ; Sets carry on F' + scf ; 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 + ret nc + ld a,h + add a,$08 + ld h,a + ;IF DISP_HIRES + ; and $18 + ; cp $18 + ;ELSE + cp $58 + ;ENDIF + ccf + ret #line 14 "draw.asm" #line 1 "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 - -SP.PixelUp: - ld a,h - dec h - and $07 - ret nz - ex af, af' - scf - ex af, af' - ld a,$08 - add a,h - ld h,a - ld a,l - sub $20 - ld l,a - ret nc - ld a,h - sub $08 - ld h,a - ;IF DISP_HIRES - ; and $18 - ; cp $18 - ; ccf - ;ELSE - cp $40 - ;ENDIF - ret + + ; + ; 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 + +SP.PixelUp: + ld a,h + dec h + and $07 + ret nz + ex af, af' + scf + ex af, af' + ld a,$08 + add a,h + ld h,a + ld a,l + sub $20 + ld l,a + ret nc + ld a,h + sub $08 + ld h,a + ;IF DISP_HIRES + ; and $18 + ; cp $18 + ; ccf + ;ELSE + cp $40 + ;ENDIF + ret #line 15 "draw.asm" #line 1 "PixelLeft.asm" - ; - ; 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 - - -SP.PixelLeft: - rlca ; Sets new pixel bit 1 to the right - ret nc - 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 - ret - + + ; + ; 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 + + +SP.PixelLeft: + rlca ; Sets new pixel bit 1 to the right + ret nc + 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 + ret + #line 16 "draw.asm" #line 1 "PixelRight.asm" - ; - ; 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 - - -SP.PixelRight: - rrca ; Sets new pixel bit 1 to the right - ret nc - 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 - ret - + + ; + ; 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 + + +SP.PixelRight: + rrca ; Sets new pixel bit 1 to the right + ret nc + 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 + ret + #line 17 "draw.asm" - + ;; DRAW PROCEDURE - PROC - + PROC + LOCAL __DRAW1 LOCAL __DRAW2 LOCAL __DRAW3 @@ -565,22 +578,22 @@ SP.PixelRight: LOCAL __INCX, __INCY, __DECX, __DECY LOCAL P_FLAG P_FLAG EQU 23697 - + __DRAW_ERROR: jp __OUT_OF_SCREEN_ERR - + DRAW: ;; ENTRY POINT - + LOCAL PIXEL_ADDR 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 @@ -588,26 +601,26 @@ DRAW: 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 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 @@ -616,7 +629,7 @@ __DRAW: ld (__PLOTINVERSE), a ld a, 0A6h ; and (hl) jp __DRAW_START - + __DRAW_SETUP1: xor a ; nop ld (__PLOTINVERSE), a @@ -624,7 +637,7 @@ __DRAW_SETUP1: bit 0, c ; Test for OVER jr z, __DRAW_START ld a, 0AEh ; xor (hl) - + __DRAW_START: ld (__PLOTOVER), a ; "Pokes" last operation exx @@ -634,7 +647,7 @@ __DRAW_START: LOCAL __PIXEL_ADDR __PIXEL_ADDR EQU 22ACh call __PIXEL_ADDR - + ;; Now gets pixel mask in A register ld b, a inc b @@ -644,7 +657,7 @@ __DRAW_START: __PIXEL_MASK: rra djnz __PIXEL_MASK - + ld b, d ; Restores B' from D' pop de ; D'E' = y2, x2 exx ; At this point: D'E' = y2,x2 coords @@ -652,147 +665,147 @@ __PIXEL_MASK: ex af, af' ; Saves A reg for later ; A' = Pixel mask ; H'L' = Screen Address of pixel - + ld bc, (COORDS) ; B,C = y1, x1 - - ld a, e + + 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 - + jr z, __DRAW4 ; if error < 0 + exx - add hl, bc ; error += dY + 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 dyncamically updated ld a, e ; Restores A reg. call __FASTPLOT - + __DRAW4_LOOP: ld a, b cp d jp nz, __DRAW3 ld (COORDS), bc - ret - + ret + __DRAW_DX_GT_DY: ; DX > DY ; -------------------------- ; HL = error = dX / 2 ld h, 0 - ld l, c + 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 ; + jr z, __DRAW6 ; exx add hl, bc ; error += dX - exx - + exx + DY2: ; y += yi inc b call __INCY ; This address will be dynamically updated - + __DRAW6: ld a, e DX2: ; x += xi @@ -800,40 +813,40 @@ DX2: ; x += xi 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 - - PIXEL_ADDR EQU 22ACh + + PIXEL_ADDR EQU 22ACh 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: @@ -841,12 +854,12 @@ __PLOTINVERSE: __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 ;; gets ATTR position with offset given in SCREEN_ADDR @@ -859,29 +872,29 @@ __PLOTOVER: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR call PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + pop de pop hl - - LOCAL __FASTPLOTEND -__FASTPLOTEND: + + LOCAL __FASTPLOTEND +__FASTPLOTEND: or a ; Resets carry flag ex af, af' ; Recovers A reg ld a, e ret - + ENDP - + #line 14 "draw3.asm" - + ; Ripped from the ZX Spectrum ROM - + DRAW3: - PROC + PROC LOCAL STACK_TO_BC LOCAL STACK_TO_A LOCAL COORDS @@ -892,12 +905,12 @@ DRAW3: LOCAL L23C1 LOCAL L2D28 LOCAL SUM_C, SUM_B - + L2D28 EQU 02D28h COORDS EQU 5C7Dh STACK_TO_BC EQU 2307h STACK_TO_A EQU 2314h - + exx ex af, af' ;; Preserves ARC pop hl @@ -910,14 +923,14 @@ DRAW3: exx ex af, af' call __FPSTACK_PUSH ;; R Arc - - ; Now enter the calculator and store the complete rotation angle in mem-5 - + + ; 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). @@ -925,39 +938,39 @@ DRAW3: 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 + ; 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 + + ; 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 + ; 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 + ; 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. @@ -968,103 +981,103 @@ DRAW3: 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-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 + ; 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 + + ; 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. - ; + ; 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. @@ -1074,17 +1087,17 @@ L23C1: CALL 247Dh ; routine CD-PRMS1 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). @@ -1096,7 +1109,7 @@ L23C1: CALL 247Dh ; routine CD-PRMS1 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. @@ -1104,131 +1117,131 @@ L23C1: CALL 247Dh ; routine CD-PRMS1 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 + + ; 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. - + 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 - + ; 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. - + + ; 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. + ; 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 + ; 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...). - +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 + ; 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 + ; 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 + ; 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 + ; 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. - + ; 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 +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 $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. @@ -1237,51 +1250,51 @@ L2425: RST 28H ;; FP-CALC 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 . - + DEFB $38 ;;end-calc . + ; Note. the calculator stack actually holds tx, ty, ax, ay - ; and the last absolute values of x and y + ; 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 + ; 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 + ; 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 $0F ;;addition ax+xr (= new ax). DEFB $31 ;;duplicate ax, ax. - DEFB $38 ;;end-calc 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). @@ -1289,59 +1302,59 @@ L2439: PUSH BC ; Preserve the arc counter on the machine stack. 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 + ; 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 + ; 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,($5C7D) ; 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,($5C7E) ; 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. @@ -1349,7 +1362,7 @@ L245F: RST 28H ;; FP-CALC tx, ty, ax, ay. 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 @@ -1357,7 +1370,7 @@ L2477: SUM_C: add a, l ld l, a ;; X2 - + rl d ;; Low sign to carry ld a, b jr nc, SUM_B @@ -1366,54 +1379,57 @@ SUM_B: add a, h ld h, a jp __DRAW ;;forward to LINE-DRAW (Fastcalled) - + ENDP #line 76 "draw3.bas" #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -1421,64 +1437,80 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 77 "draw3.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/dup_func_decl.bas b/tests/functional/dup_func_decl.bas new file mode 100644 index 000000000..175cd8ddd --- /dev/null +++ b/tests/functional/dup_func_decl.bas @@ -0,0 +1,7 @@ + + +declare function f() as float + +declare function f() + + diff --git a/tests/functional/einar01.asm b/tests/functional/einar01.asm index 6e2457f2c..edf14cd2d 100644 --- a/tests/functional/einar01.asm +++ b/tests/functional/einar01.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _score: DEFW 0000h diff --git a/tests/functional/einarattr.asm b/tests/functional/einarattr.asm index 1339d1f55..abc4773f7 100644 --- a/tests/functional/einarattr.asm +++ b/tests/functional/einarattr.asm @@ -57,17 +57,20 @@ __LABEL1: DEFW 0001h DEFB 41h #line 1 "copy_attr.asm" - + + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -75,45 +78,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -126,41 +130,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -168,12 +174,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -181,50 +188,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -233,15 +241,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -249,26 +259,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -282,44 +292,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -330,27 +341,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -358,27 +370,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -387,40 +400,41 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register - - - + + + 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 @@ -430,7 +444,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -438,19 +452,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -460,7 +475,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -471,18 +486,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -492,7 +508,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -503,18 +519,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -524,7 +541,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -537,21 +554,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -560,79 +578,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -641,75 +659,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -719,7 +737,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -731,16 +749,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -754,49 +772,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -807,17 +825,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -830,27 +848,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -858,10 +876,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -878,80 +896,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -968,8 +986,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -993,17 +1011,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1014,7 +1032,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1024,21 +1042,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1057,9 +1075,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1084,72 +1102,72 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 3 "copy_attr.asm" -#line 4 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1158,20 +1176,22 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 44 "einarattr.bas" - - + + #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1179,25 +1199,25 @@ __REFRESH_TMP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1206,40 +1226,41 @@ __REFRESH_TMP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1247,25 +1268,25 @@ __REFRESH_TMP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1274,39 +1295,39 @@ __REFRESH_TMP: ; | (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 + ; 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 @@ -1314,56 +1335,56 @@ __REFRESH_TMP: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1372,57 +1393,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1431,47 +1452,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1481,51 +1502,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1534,11 +1555,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 47 "einarattr.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/einarshift.asm b/tests/functional/einarshift.asm index 329aabbe1..faf044fee 100644 --- a/tests/functional/einarshift.asm +++ b/tests/functional/einarshift.asm @@ -42,15 +42,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -58,45 +60,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -109,41 +112,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -151,12 +156,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -164,50 +170,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -216,15 +223,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -232,26 +241,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -265,44 +274,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -313,27 +323,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -341,27 +352,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -370,81 +382,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -453,32 +467,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -488,7 +502,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -496,19 +510,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -518,7 +533,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -529,18 +544,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -550,7 +566,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -561,18 +577,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -582,7 +599,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -595,21 +612,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -618,79 +636,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -699,75 +717,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -777,7 +795,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -789,16 +807,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -812,49 +830,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -865,17 +883,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -888,27 +906,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -916,10 +934,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -936,80 +954,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1026,8 +1044,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1051,17 +1069,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1072,7 +1090,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1082,21 +1100,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1115,9 +1133,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1142,30 +1160,33 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 32 "einarshift.bas" #line 1 "printu8.asm" + #line 1 "printi8.asm" + #line 1 "printnum.asm" - - - + + + + __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 @@ -1173,62 +1194,63 @@ __PRINTU_CONT: pop bc djnz __PRINTU_CONT ret - + ENDP - - + + __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER mus preserve registers ld a, '-' jp __PRINT_DIGIT - + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - + + #line 2 "printi8.asm" #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -1236,84 +1258,84 @@ __DIV8A: 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 - + 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 - + #line 3 "printi8.asm" - + __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 - + #line 2 "printu8.asm" - + #line 33 "einarshift.bas" - + ZXBASIC_USER_DATA: _a: DEFB 03h diff --git a/tests/functional/elseif.asm b/tests/functional/elseif.asm index 5b509b7a9..671802e01 100644 --- a/tests/functional/elseif.asm +++ b/tests/functional/elseif.asm @@ -44,7 +44,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _num: DEFB 00 diff --git a/tests/functional/elseif1.asm b/tests/functional/elseif1.asm index 225aebe3f..237c470ee 100644 --- a/tests/functional/elseif1.asm +++ b/tests/functional/elseif1.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/elseif2.asm b/tests/functional/elseif2.asm index 225aebe3f..237c470ee 100644 --- a/tests/functional/elseif2.asm +++ b/tests/functional/elseif2.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/elseif3.asm b/tests/functional/elseif3.asm index eae53c51f..135800f17 100644 --- a/tests/functional/elseif3.asm +++ b/tests/functional/elseif3.asm @@ -21,9 +21,8 @@ __LABEL0: call __LTI8 or a jp z, __LABEL3 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL3: __LABEL1: ld hl, 0 @@ -43,7 +42,9 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti8.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -52,10 +53,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -66,8 +67,8 @@ checkParity: ret ENDP #line 2 "lti8.asm" -#line 34 "elseif3.bas" - +#line 33 "elseif3.bas" + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/elseif4.asm b/tests/functional/elseif4.asm index 225aebe3f..237c470ee 100644 --- a/tests/functional/elseif4.asm +++ b/tests/functional/elseif4.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/elseif5.asm b/tests/functional/elseif5.asm index 4ff82f6c2..c80afa493 100644 --- a/tests/functional/elseif5.asm +++ b/tests/functional/elseif5.asm @@ -20,13 +20,13 @@ __LABEL0: ld hl, (_a - 1) call __LTI8 or a - jp z, __LABEL3 + jp nz, __LABEL3 +__LABEL2: ld a, (_a) or a jp nz, __LABEL5 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL5: __LABEL3: __LABEL1: @@ -47,7 +47,9 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti8.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -56,10 +58,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -71,7 +73,7 @@ checkParity: ENDP #line 2 "lti8.asm" #line 38 "elseif5.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/elseif6.asm b/tests/functional/elseif6.asm index 50aba929c..91f68b964 100644 --- a/tests/functional/elseif6.asm +++ b/tests/functional/elseif6.asm @@ -15,9 +15,8 @@ __START_PROGRAM: call __LTI8 or a jp z, __LABEL1 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL1: ld hl, 0 ld b, h @@ -36,7 +35,9 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti8.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -45,10 +46,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -59,8 +60,8 @@ checkParity: ret ENDP #line 2 "lti8.asm" -#line 27 "elseif6.bas" - +#line 26 "elseif6.bas" + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/emook0.bi b/tests/functional/emook0.bi new file mode 100644 index 000000000..980656268 --- /dev/null +++ b/tests/functional/emook0.bi @@ -0,0 +1,8 @@ +#define SetREG(reg,val) \ + LD A,reg \ + LD BC,val + +ASM + SetREG(A,254) +end Asm + diff --git a/tests/functional/emook0.out b/tests/functional/emook0.out new file mode 100644 index 000000000..7fb90e2bb --- /dev/null +++ b/tests/functional/emook0.out @@ -0,0 +1,11 @@ +#line 1 "emook0.bi" + + +ASM + + LD A,A + LD BC,254 +#line 6 + +end Asm + diff --git a/tests/functional/emptystrparam.asm b/tests/functional/emptystrparam.asm index 9acb6cee4..49cd5e90e 100644 --- a/tests/functional/emptystrparam.asm +++ b/tests/functional/emptystrparam.asm @@ -55,9 +55,10 @@ _stringtest__leave: __LABEL0: DEFW 0000h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -65,25 +66,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -92,40 +93,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -133,25 +135,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -160,39 +162,39 @@ __LABEL0: ; | (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 + ; 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 @@ -200,56 +202,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -258,57 +260,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -317,47 +319,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -367,15 +369,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 43 "emptystrparam.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -383,25 +387,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -410,50 +414,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -461,12 +466,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -474,16 +480,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -495,39 +501,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -535,15 +541,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -568,14 +574,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -583,25 +589,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -610,37 +616,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 44 "emptystrparam.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/end.asm b/tests/functional/end.asm index 14456c1dc..f7f8d7a68 100644 --- a/tests/functional/end.asm +++ b/tests/functional/end.asm @@ -30,7 +30,7 @@ __CALL_BACK__: ld b, h ld c, l jp __END_PROGRAM - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/endif.asm b/tests/functional/endif.asm new file mode 100644 index 000000000..2759c7538 --- /dev/null +++ b/tests/functional/endif.asm @@ -0,0 +1,40 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + jp __LABEL1 +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/endif.bas b/tests/functional/endif.bas new file mode 100644 index 000000000..2c74e2524 --- /dev/null +++ b/tests/functional/endif.bas @@ -0,0 +1,7 @@ + +DIM a as UByte + +IF a < 0 THEN + a = a + 1 +ENDIF + diff --git a/tests/functional/eq0.asm b/tests/functional/eq0.asm index 2bff70a7d..50601daab 100644 --- a/tests/functional/eq0.asm +++ b/tests/functional/eq0.asm @@ -34,38 +34,41 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "eqf.asm" + #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -74,35 +77,35 @@ __I8TOFREG: 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 @@ -110,20 +113,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -131,45 +134,47 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 2 "eqf.asm" #line 1 "ftou32reg.asm" - - + + + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -177,95 +182,112 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 3 "eqf.asm" #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -281,38 +303,39 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 4 "eqf.asm" - + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - - + + __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 + + call __FPSTACK_POP jp __FTOU8 ; Convert to 8 bits - + #line 25 "eq0.bas" #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -333,10 +356,10 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - + + #line 26 "eq0.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/equ8.asm b/tests/functional/equ8.asm index e70c68b73..18dd25220 100644 --- a/tests/functional/equ8.asm +++ b/tests/functional/equ8.asm @@ -13,9 +13,8 @@ __START_PROGRAM: ld a, (_a) sub 5 jp nz, __LABEL1 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL1: ld hl, 0 ld b, h @@ -33,7 +32,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/errletfunc.bas b/tests/functional/errletfunc.bas new file mode 100644 index 000000000..af5dcfb8f --- /dev/null +++ b/tests/functional/errletfunc.bas @@ -0,0 +1,6 @@ + +SUB x +END SUB + +let x = 1 + diff --git a/tests/functional/fact.asm b/tests/functional/fact.asm new file mode 100644 index 000000000..a068dea03 --- /dev/null +++ b/tests/functional/fact.asm @@ -0,0 +1,2158 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + call CLS + ld a, 1 + ld (_x), a + jp __LABEL0 +__LABEL3: + ld hl, __LABEL5 + xor a + call __PRINTSTR + ld a, (_x) + call __PRINTU8 + ld hl, __LABEL6 + xor a + call __PRINTSTR + ld a, (_x) + ld l, a + ld h, 0 + ld e, h + ld d, h + push de + push hl + call _fact + call __PRINTU32 + call PRINT_EOL +__LABEL4: + ld hl, _x + inc (hl) +__LABEL0: + ld a, 10 + ld hl, (_x - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_fact: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld de, 0 + ld hl, 2 + call __SUB32 + jp nc, __LABEL8 + ld de, 0 + ld hl, 1 + jp _fact__leave +__LABEL8: + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld de, 0 + ld hl, 1 + call __SUB32 + push de + push hl + call _fact + call __MUL32 +_fact__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +__LABEL5: + DEFW 0006h + DEFB 46h + DEFB 61h + DEFB 63h + DEFB 74h + DEFB 20h + DEFB 28h +__LABEL6: + DEFW 0004h + DEFB 29h + DEFB 20h + DEFB 3Dh + DEFB 20h +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + +#line 1 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 9 "cls.asm" + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 110 "fact.bas" +#line 1 "mul32.asm" + +#line 1 "_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 + + +__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: + and a ; reset carry flag + sbc hl,hl ; result bits 32..47 = 0 + exx + sbc hl,hl ; result bits 48..63 = 0 + 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 + +#line 2 "mul32.asm" + +__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 + + +#line 111 "fact.bas" +#line 1 "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 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 112 "fact.bas" +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 113 "fact.bas" +#line 1 "printu32.asm" + +#line 1 "printi32.asm" + +#line 1 "printnum.asm" + + + + +__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 mus preserve registers + ld a, '-' + jp __PRINT_DIGIT + + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + + +#line 2 "printi32.asm" +#line 1 "neg32.asm" + +__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 + +#line 3 "printi32.asm" +#line 1 "div32.asm" + + + + ; --------------------------------------------------------- +__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 + +#line 4 "printi32.asm" + + + +__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 + +#line 2 "printu32.asm" + +#line 114 "fact.bas" +#line 1 "printu8.asm" + +#line 1 "printi8.asm" + + +#line 1 "div8.asm" + + ; -------------------------------- +__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 + +#line 3 "printi8.asm" + +__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 + +#line 2 "printu8.asm" + +#line 115 "fact.bas" +#line 1 "sub32.asm" + + ; 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 + +__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 +#line 116 "fact.bas" + +ZXBASIC_USER_DATA: +_x: + DEFB 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/fact.bas b/tests/functional/fact.bas new file mode 100644 index 000000000..3e1a0a5cd --- /dev/null +++ b/tests/functional/fact.bas @@ -0,0 +1,15 @@ +REM Factorial recursive test + +function fact(x as ulong) as ulong + if x < 2 then + return 1 + end if + + return x * fact(x - 1) +end function + +cls +for x = 1 To 10: + print "Fact ("; x; ") = "; fact(x) +next x + diff --git a/tests/functional/fastcall0.asm b/tests/functional/fastcall0.asm index 325f8acfb..ef260a968 100644 --- a/tests/functional/fastcall0.asm +++ b/tests/functional/fastcall0.asm @@ -65,7 +65,7 @@ _SPFill: call SPPFill_start pop ix ret -#line 1 "PixelUp.asm" +#line 1 "/src/zxb/trunk/library-asm/SP/PixelUp.asm" SP.PixelUp: ld a,h dec h @@ -86,8 +86,8 @@ _SPFill: ld h,a cp $40 ret -#line 31 "Fill.bas" -#line 1 "PixelDown.asm" +#line 31 "/src/zxb/trunk/library/SP/Fill.bas" +#line 1 "/src/zxb/trunk/library-asm/SP/PixelDown.asm" SP.PixelDown: inc h ld a,h @@ -109,8 +109,8 @@ _SPFill: cp $58 ccf ret -#line 32 "Fill.bas" -#line 1 "CharLeft.asm" +#line 32 "/src/zxb/trunk/library/SP/Fill.bas" +#line 1 "/src/zxb/trunk/library-asm/SP/CharLeft.asm" SP.CharLeft: ld a,l dec l @@ -121,8 +121,8 @@ _SPFill: ld h,a cp $40 ret -#line 33 "Fill.bas" -#line 1 "CharRight.asm" +#line 33 "/src/zxb/trunk/library/SP/Fill.bas" +#line 1 "/src/zxb/trunk/library-asm/SP/CharRight.asm" SP.CharRight: inc l ret nz @@ -132,8 +132,8 @@ _SPFill: cp $58 ccf ret -#line 34 "Fill.bas" -#line 1 "GetScrnAddr.asm" +#line 34 "/src/zxb/trunk/library/SP/Fill.bas" +#line 1 "/src/zxb/trunk/library-asm/SP/GetScrnAddr.asm" SPGetScrnAddr: and $07 or $40 @@ -165,7 +165,7 @@ norotate: or l ld e,a ret -#line 35 "Fill.bas" +#line 35 "/src/zxb/trunk/library/SP/Fill.bas" SPPFill_IXBuffer: DEFB 0,0 SPPFill_start: @@ -481,23 +481,25 @@ __LABEL0: DEFW 0001h DEFB 61h #line 1 "circle.asm" + ; Bresenham's like circle algorithm ; best known as Middle Point Circle drawing algorithm - + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -505,12 +507,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -518,7 +521,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -526,19 +529,22 @@ __STOP: ret #line 5 "circle.asm" #line 1 "plot.asm" + ; 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 - - + + #line 1 "in_screen.asm" + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -546,74 +552,75 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 2 "in_screen.asm" - - + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 9 "plot.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -626,48 +633,48 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 10 "plot.asm" - + 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 - + ld c, h + 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 @@ -675,7 +682,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) res 6, h ; Starts from 0 ld bc, (SCREEN_ADDR) add hl, bc ; Now current offset - + ld b, a inc b ld a, 0FEh @@ -683,7 +690,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) __PLOT_LOOP: rrca djnz __PLOT_LOOP - + ld b, a ld a, (P_FLAG) ld c, a @@ -691,18 +698,18 @@ __PLOT_LOOP: 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 - + ;; gets ATTR position with offset given in SCREEN_ADDR ld a, h rrca @@ -713,36 +720,36 @@ __PLOT_END: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR jp PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + __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 + PIXEL_ADDR EQU 22ACh COORDS EQU 5C7Dh ENDP #line 6 "circle.asm" - - + + ; Draws a circle at X, Y of radius R ; X, Y on the Stack, R in accumulator (Byte) - + PROC LOCAL __CIRCLE_ERROR LOCAL __CIRCLE_LOOP LOCAL __CIRCLE_NEXT - + __CIRCLE_ERROR: jp __OUT_OF_SCREEN_ERR ;; __CIRCLE_ERROR EQU __OUT_OF_SCREEN_ERR ;; __CIRCLE_ERROR: ;; ; Jumps here if out of screen ;; scf ; Always sets carry Flag - ;; + ;; ;; ld a, ERROR_OutOfScreen ;; ld (ERR_NR), a ;; ret @@ -752,95 +759,95 @@ CIRCLE: pop de ; D = Y ex (sp), hl ; __CALLEE__ convention ld e, h ; E = X - - - ld h, a ; H = R + + + ld h, a ; H = R 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 + 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 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 @@ -850,11 +857,11 @@ __CIRCLE_NEXT: ld h, a pop af ex af, af' - - push bc + + push bc exx pop hl ; H'L' = Y, X - + ld a, d add a, h ld b, a ; B = y0 + y @@ -862,7 +869,7 @@ __CIRCLE_NEXT: 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 @@ -870,7 +877,7 @@ __CIRCLE_NEXT: 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 @@ -878,7 +885,7 @@ __CIRCLE_NEXT: 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 @@ -886,76 +893,79 @@ __CIRCLE_NEXT: sub l ld c, a ; C = x0 - x call __CIRCLE_PLOT ; plot(x0 - x, y0 - y) - + ld a, d add a, l ld b, a ; B = y0 + x - ld a, e + 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 + 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 + 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 + ld a, e sub h ld c, a ; C = x0 + y call __CIRCLE_PLOT ; plot(x0 - y, y0 - x) - + exx jp __CIRCLE_LOOP - - - + + + __CIRCLE_PLOT: ; Plots a point of the circle, preserving HL and DE push hl push de - call __PLOT + call __PLOT pop de pop hl ret - + ENDP #line 469 "fastcall0.bas" - + #line 1 "pause.asm" + ; The PAUSE statement (Calling the ROM) - + __PAUSE: ld b, h ld c, l jp 1F3Dh ; PAUSE_1 #line 471 "fastcall0.bas" #line 1 "usr_str.asm" + ; 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) - - + + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -963,12 +973,13 @@ __PAUSE: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 10 "usr_str.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -976,25 +987,25 @@ __PAUSE: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1003,40 +1014,41 @@ __PAUSE: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1044,25 +1056,25 @@ __PAUSE: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1071,39 +1083,39 @@ __PAUSE: ; | (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 + ; 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 @@ -1111,56 +1123,56 @@ __PAUSE: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1169,57 +1181,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1228,47 +1240,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1278,27 +1290,27 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 11 "usr_str.asm" - + USR_STR: 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 11011111b ; Convert it to UPPER CASE @@ -1310,31 +1322,31 @@ USR_STR: 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 - + #line 472 "fastcall0.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/file_macro.asm b/tests/functional/file_macro.asm new file mode 100644 index 000000000..943dd8e4a --- /dev/null +++ b/tests/functional/file_macro.asm @@ -0,0 +1,910 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + call __STORE_STR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Eh + DEFB 66h + DEFB 69h + DEFB 6Ch + DEFB 65h + DEFB 5Fh + DEFB 6Dh + DEFB 61h + DEFB 63h + DEFB 72h + DEFB 6Fh + DEFB 2Eh + DEFB 62h + DEFB 61h + DEFB 73h +#line 1 "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 "strcpy.asm" + +#line 1 "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. + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 70 "realloc.asm" +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 71 "realloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 72 "realloc.asm" + + + ; --------------------------------------------------------------------- + ; 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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 37 "file_macro.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/file_macro.bas b/tests/functional/file_macro.bas new file mode 100644 index 000000000..ce6ece335 --- /dev/null +++ b/tests/functional/file_macro.bas @@ -0,0 +1,5 @@ + +#line 3 "file_macro.bas" +LET a$ = __FILE__ + + diff --git a/tests/functional/for0.asm b/tests/functional/for0.asm index 4ce41fea3..6b9cc7e5b 100644 --- a/tests/functional/for0.asm +++ b/tests/functional/for0.asm @@ -25,9 +25,8 @@ __LABEL3: xor a call __PRINTSTR __LABEL4: - ld a, (_x) - inc a - ld (_x), a + ld hl, _x + inc (hl) __LABEL0: ld a, 126 ld hl, (_x - 1) @@ -55,18 +54,20 @@ __LABEL5: DEFW 0001h DEFB 20h #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -74,36 +75,36 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 9 "cls.asm" - + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -116,27 +117,29 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - -#line 42 "for0.bas" + +#line 41 "for0.bas" #line 1 "lti8.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -145,10 +148,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -159,32 +162,37 @@ checkParity: ret ENDP #line 2 "lti8.asm" -#line 43 "for0.bas" +#line 42 "for0.bas" #line 1 "printi8.asm" + #line 1 "printnum.asm" + #line 1 "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 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -192,12 +200,13 @@ checkParity: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -205,50 +214,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -257,15 +267,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -273,26 +285,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -306,44 +318,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -354,27 +367,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -382,27 +396,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -411,81 +426,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -494,32 +511,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -529,7 +546,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -537,19 +554,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -559,7 +577,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -570,18 +588,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -591,7 +610,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -602,18 +621,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -623,7 +643,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -636,21 +656,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -659,79 +680,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -740,75 +761,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -818,7 +839,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -830,16 +851,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -853,49 +874,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -906,17 +927,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -929,27 +950,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -957,10 +978,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -977,80 +998,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1067,8 +1088,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1092,17 +1113,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1113,7 +1134,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1123,21 +1144,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1156,9 +1177,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1183,26 +1204,26 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 2 "printnum.asm" - - + + __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 @@ -1210,62 +1231,63 @@ __PRINTU_CONT: pop bc djnz __PRINTU_CONT ret - + ENDP - - + + __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER mus preserve registers ld a, '-' jp __PRINT_DIGIT - + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - + + #line 2 "printi8.asm" #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -1273,89 +1295,91 @@ __DIV8A: 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 - + 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 - + #line 3 "printi8.asm" - + __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 - -#line 44 "for0.bas" + +#line 43 "for0.bas" #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1363,25 +1387,25 @@ __PRINTU_LOOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1390,40 +1414,41 @@ __PRINTU_LOOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1431,25 +1456,25 @@ __PRINTU_LOOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1458,39 +1483,39 @@ __PRINTU_LOOP: ; | (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 + ; 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 @@ -1498,56 +1523,56 @@ __PRINTU_LOOP: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1556,57 +1581,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1615,47 +1640,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1665,51 +1690,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1718,11 +1743,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - -#line 45 "for0.bas" - + +#line 44 "for0.bas" + ZXBASIC_USER_DATA: _x: DEFB 00 diff --git a/tests/functional/forempty.asm b/tests/functional/forempty.asm new file mode 100644 index 000000000..75d67897c --- /dev/null +++ b/tests/functional/forempty.asm @@ -0,0 +1,77 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + xor a + ld (_a), a + jp __LABEL0 +__LABEL3: +__LABEL4: + ld a, (_a) +__LABEL0: + ld a, 1 + ld hl, (_a - 1) + call __LTI8 + or a + jp z, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 31 "forempty.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/forempty.bas b/tests/functional/forempty.bas new file mode 100644 index 000000000..311e4967c --- /dev/null +++ b/tests/functional/forempty.bas @@ -0,0 +1,5 @@ + +DIM a as Byte + +FOR a = 0 TO 1 STEP 0: NEXT + diff --git a/tests/functional/forline.bas b/tests/functional/forline.bas new file mode 100644 index 000000000..bb0f89503 --- /dev/null +++ b/tests/functional/forline.bas @@ -0,0 +1,2 @@ + +FOR i = 1 TO 10 PRINT "HOLA" NEXT diff --git a/tests/functional/fornext.asm b/tests/functional/fornext.asm index 071756e87..c7905c56e 100644 --- a/tests/functional/fornext.asm +++ b/tests/functional/fornext.asm @@ -17,9 +17,8 @@ __LABEL__10: __LABEL3: __LABEL__20: __LABEL4: - ld a, (_i) - inc a - ld (_i), a + ld hl, _i + inc (hl) __LABEL0: ld a, 10 ld hl, (_i - 1) @@ -33,9 +32,8 @@ __LABEL__30: __LABEL8: __LABEL__40: __LABEL9: - ld a, (_i) - inc a - ld (_i), a + ld hl, _i + inc (hl) __LABEL5: ld a, 10 ld hl, (_i - 1) @@ -47,9 +45,8 @@ __LABEL7: jp __LABEL10 __LABEL13: __LABEL14: - ld a, (_i) - inc a - ld (_i), a + ld hl, _i + inc (hl) __LABEL10: ld a, 10 ld hl, (_i - 1) @@ -61,9 +58,8 @@ __LABEL12: jp __LABEL15 __LABEL18: __LABEL19: - ld a, (_i) - inc a - ld (_i), a + ld hl, _i + inc (hl) __LABEL15: ld a, 10 ld hl, (_i - 1) @@ -86,7 +82,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _i: DEFB 00 diff --git a/tests/functional/fornext2.asm b/tests/functional/fornext2.asm index 99b98db31..99fa3d77f 100644 --- a/tests/functional/fornext2.asm +++ b/tests/functional/fornext2.asm @@ -37,9 +37,8 @@ _test: jp __LABEL0 __LABEL3: __LABEL4: - ld a, (_x) - inc a - ld (_x), a + ld hl, _x + inc (hl) __LABEL0: ld a, 6 ld hl, (_x - 1) @@ -50,7 +49,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _x: DEFB 00 diff --git a/tests/functional/fornext3.asm b/tests/functional/fornext3.asm index f466e2302..a2c98fed0 100644 --- a/tests/functional/fornext3.asm +++ b/tests/functional/fornext3.asm @@ -41,7 +41,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _i: DEFB 00, 00 diff --git a/tests/functional/fornextopt.asm b/tests/functional/fornextopt.asm new file mode 100644 index 000000000..9b0fb0bcd --- /dev/null +++ b/tests/functional/fornextopt.asm @@ -0,0 +1,51 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 1 + ld (_a), a + jp __LABEL0 +__LABEL3: +__LABEL__lbl: + ld hl, _a + inc (hl) +__LABEL4: + ld hl, _a + inc (hl) +__LABEL0: + xor a + ld hl, (_a - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + jp __LABEL__lbl +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/fornextopt.bas b/tests/functional/fornextopt.bas new file mode 100644 index 000000000..c355ed89f --- /dev/null +++ b/tests/functional/fornextopt.bas @@ -0,0 +1,9 @@ + +DIM a as UByte + +FOR a = 1 TO 0 +lbl: +a = a + 1 +NEXT a + +GOTO lbl diff --git a/tests/functional/fornextopt2.asm b/tests/functional/fornextopt2.asm new file mode 100644 index 000000000..7bebdf5c6 --- /dev/null +++ b/tests/functional/fornextopt2.asm @@ -0,0 +1,50 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 10 + ld (_a), a + jp __LABEL0 +__LABEL3: +__LABEL__lbl: + ld hl, _a + inc (hl) +__LABEL4: + ld hl, _a + dec (hl) +__LABEL0: + ld a, (_a) + cp 11 + jp nc, __LABEL3 +__LABEL2: + jp __LABEL__lbl +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/fornextopt2.bas b/tests/functional/fornextopt2.bas new file mode 100644 index 000000000..c1a9c963b --- /dev/null +++ b/tests/functional/fornextopt2.bas @@ -0,0 +1,9 @@ + +DIM a as UByte + +FOR a = 10 TO 11 STEP -1 +lbl: +a = a + 1 +NEXT a + +GOTO lbl diff --git a/tests/functional/fornextopt3.asm b/tests/functional/fornextopt3.asm new file mode 100644 index 000000000..237c470ee --- /dev/null +++ b/tests/functional/fornextopt3.asm @@ -0,0 +1,37 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/fornextopt3.bas b/tests/functional/fornextopt3.bas new file mode 100644 index 000000000..e5af8ec85 --- /dev/null +++ b/tests/functional/fornextopt3.bas @@ -0,0 +1,8 @@ + +DIM a as UByte + +FOR a = 1 TO 0 +lbl: +a = a + 1 +NEXT a + diff --git a/tests/functional/fornextopt4.asm b/tests/functional/fornextopt4.asm new file mode 100644 index 000000000..237c470ee --- /dev/null +++ b/tests/functional/fornextopt4.asm @@ -0,0 +1,37 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/fornextopt4.bas b/tests/functional/fornextopt4.bas new file mode 100644 index 000000000..72ad68be3 --- /dev/null +++ b/tests/functional/fornextopt4.bas @@ -0,0 +1,8 @@ + +DIM a as UByte + +FOR a = 10 TO 11 STEP -1 +lbl: +a = a + 1 +NEXT a + diff --git a/tests/functional/forsplitted.asm b/tests/functional/forsplitted.asm new file mode 100644 index 000000000..ccbbb0e07 --- /dev/null +++ b/tests/functional/forsplitted.asm @@ -0,0 +1,71 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__30: + ld a, 1 + ld (_i), a + jp __LABEL0 +__LABEL3: + ld a, 1 + ld (_m), a + jp __LABEL5 +__LABEL8: +__LABEL__40: + xor a + ld (_M), a +__LABEL9: + ld hl, _m + inc (hl) +__LABEL5: + ld a, 6 + ld hl, (_m - 1) + cp h + jp nc, __LABEL8 +__LABEL7: +__LABEL4: + ld hl, _i + inc (hl) +__LABEL0: + ld a, 8 + ld hl, (_i - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_i: + DEFB 00 +_m: + DEFB 00 +_M: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/forsplitted.bas b/tests/functional/forsplitted.bas new file mode 100644 index 000000000..8591894c8 --- /dev/null +++ b/tests/functional/forsplitted.bas @@ -0,0 +1,5 @@ + +30 FOR i=1 TO 8: FOR m=1 TO 6 +40 LET M=0: NEXT m: NEXT i + + diff --git a/tests/functional/forsplitted0.asm b/tests/functional/forsplitted0.asm new file mode 100644 index 000000000..bf3649445 --- /dev/null +++ b/tests/functional/forsplitted0.asm @@ -0,0 +1,83 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__10: +__LABEL__20: + ld a, 1 + ld (_i), a + jp __LABEL0 +__LABEL3: +__LABEL__30: + ld hl, _i + inc (hl) +__LABEL4: + ld hl, _i + inc (hl) +__LABEL0: + ld a, 10 + ld hl, (_i - 1) + call __LTI8 + or a + jp z, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 37 "forsplitted0.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/forsplitted0.bas b/tests/functional/forsplitted0.bas new file mode 100644 index 000000000..398e28841 --- /dev/null +++ b/tests/functional/forsplitted0.bas @@ -0,0 +1,4 @@ +10 DIM i as Byte +20 FOR i = 1 TO 10 +30 LET i = i + 1: NEXT i + diff --git a/tests/functional/forsplitted1.asm b/tests/functional/forsplitted1.asm new file mode 100644 index 000000000..4969356b9 --- /dev/null +++ b/tests/functional/forsplitted1.asm @@ -0,0 +1,69 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 1 + ld (_i), a + jp __LABEL0 +__LABEL3: + ld a, 1 + ld (_m), a + jp __LABEL5 +__LABEL8: + xor a + ld (_M), a +__LABEL9: + ld hl, _m + inc (hl) +__LABEL5: + ld a, 6 + ld hl, (_m - 1) + cp h + jp nc, __LABEL8 +__LABEL7: +__LABEL4: + ld hl, _i + inc (hl) +__LABEL0: + ld a, 8 + ld hl, (_i - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_i: + DEFB 00 +_m: + DEFB 00 +_M: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/forsplitted1.bas b/tests/functional/forsplitted1.bas new file mode 100644 index 000000000..04e37e65f --- /dev/null +++ b/tests/functional/forsplitted1.bas @@ -0,0 +1,5 @@ + +FOR i=1 TO 8: FOR m=1 TO 6 +LET M=0: NEXT m: NEXT i + + diff --git a/tests/functional/fporder.asm b/tests/functional/fporder.asm index 9afd4f40d..f9f35818f 100644 --- a/tests/functional/fporder.asm +++ b/tests/functional/fporder.asm @@ -43,38 +43,40 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "cos.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -90,20 +92,21 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "cos.asm" - + COS: ; Computes COS using ROM FP-CALC call __FPSTACK_PUSH - + rst 28h ; ROM CALC defb 20h ; COS defb 38h ; END CALC - + jp __FPSTACK_POP - + #line 34 "fporder.bas" #line 1 "mulf.asm" - - + + + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -112,25 +115,26 @@ COS: ; Computes COS using ROM FP-CALC ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __MULF: ; Multiplication call __FPSTACK_PUSH2 - + ; ------------- ROM MUL rst 28h - defb 04h ; + defb 04h ; defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 35 "fporder.bas" #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -151,10 +155,11 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - + + #line 36 "fporder.bas" #line 1 "storef.asm" + __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 @@ -162,7 +167,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -170,7 +175,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -182,35 +187,36 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 37 "fporder.bas" #line 1 "subf.asm" - - + + + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - - + + __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 - + #line 38 "fporder.bas" - + ZXBASIC_USER_DATA: _n: DEFB 81h diff --git a/tests/functional/func0.asm b/tests/functional/func0.asm new file mode 100644 index 000000000..c6df31a38 --- /dev/null +++ b/tests/functional/func0.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_f: + push ix + ld ix, 0 + add ix, sp +_f__leave: + ld sp, ix + pop ix + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/func0.bas b/tests/functional/func0.bas new file mode 100644 index 000000000..662b647d9 --- /dev/null +++ b/tests/functional/func0.bas @@ -0,0 +1,6 @@ +' simple func declaration with default float type +' should issue a warning + +function f() +end function + diff --git a/tests/functional/funccall0.asm b/tests/functional/funccall0.asm index bb1fe8a00..790414435 100644 --- a/tests/functional/funccall0.asm +++ b/tests/functional/funccall0.asm @@ -47,7 +47,7 @@ _test__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/funccall1.asm b/tests/functional/funccall1.asm index 03195bd50..9c4ed0c5b 100644 --- a/tests/functional/funccall1.asm +++ b/tests/functional/funccall1.asm @@ -49,7 +49,7 @@ _test2__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/funccall2.asm b/tests/functional/funccall2.asm index ff087c942..fd97ec6f2 100644 --- a/tests/functional/funccall2.asm +++ b/tests/functional/funccall2.asm @@ -41,7 +41,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/funcif.asm b/tests/functional/funcif.asm new file mode 100644 index 000000000..ea11b257d --- /dev/null +++ b/tests/functional/funcif.asm @@ -0,0 +1,213 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_y) + ld de, (_y + 1) + ld bc, (_y + 3) + call __FTOU32REG + ld a, l + push af + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call __FTOU32REG + ld a, l + push af + call _ScanNear + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_ScanNear: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + ld a, (ix+5) + dec a + sub 1 + sbc a, a + push af + ld a, (ix+7) + dec a + sub 1 + sbc a, a + ld h, a + pop af + or h + jp z, __LABEL1 + ld (ix-1), 1 +__LABEL1: + ld a, (ix-1) + jp _ScanNear__leave +_ScanNear__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 64 "funcif.bas" + +ZXBASIC_USER_DATA: +_x: + DEFB 00, 00, 00, 00, 00 +_y: + DEFB 00, 00, 00, 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/funcif.bas b/tests/functional/funcif.bas new file mode 100644 index 000000000..fdeae2afd --- /dev/null +++ b/tests/functional/funcif.bas @@ -0,0 +1,10 @@ + +function ScanNear(x as ubyte,y as ubyte) as ubyte +'This scans next fields of x,y until figure for king or pawn + dim result as ubyte + if x=1 or y=1 then result=1:end if + return result +end Function + +ScanNear(x, y) + diff --git a/tests/functional/funcnoparm.asm b/tests/functional/funcnoparm.asm new file mode 100644 index 000000000..9cccc44b4 --- /dev/null +++ b/tests/functional/funcnoparm.asm @@ -0,0 +1,60 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 1 + push af + call _xx + ld (_c), hl + ld a, 2 + push af + call _xx + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_xx: + push ix + ld ix, 0 + add ix, sp + ld a, (ix+5) + inc a + ld l, a + ld h, 0 +_xx__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + +ZXBASIC_USER_DATA: +_c: + DEFB 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/funcnoparm.bas b/tests/functional/funcnoparm.bas new file mode 100644 index 000000000..662d1d47e --- /dev/null +++ b/tests/functional/funcnoparm.bas @@ -0,0 +1,8 @@ + +FUNCTION xx(a as Ubyte) as UInteger + RETURN a + 1 +END FUNCTION + +LET c = xx 1 +xx 2 + diff --git a/tests/functional/funcnoparm2.bas b/tests/functional/funcnoparm2.bas new file mode 100644 index 000000000..c0a8c54f3 --- /dev/null +++ b/tests/functional/funcnoparm2.bas @@ -0,0 +1,9 @@ + +FUNCTION xx(a as Ubyte, b as Ubyte) as UInteger + RETURN a + 1 +END FUNCTION + +LET c = xx 1, 2 +xx 2 3 + + diff --git a/tests/functional/fwd_func_decl.asm b/tests/functional/fwd_func_decl.asm new file mode 100644 index 000000000..7c53d3721 --- /dev/null +++ b/tests/functional/fwd_func_decl.asm @@ -0,0 +1,51 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_f: + push ix + ld ix, 0 + add ix, sp +_f__leave: + ld sp, ix + pop ix + ret +_q: + push ix + ld ix, 0 + add ix, sp +_q__leave: + ld sp, ix + pop ix + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/fwd_func_decl.bas b/tests/functional/fwd_func_decl.bas new file mode 100644 index 000000000..dfe29bd90 --- /dev/null +++ b/tests/functional/fwd_func_decl.bas @@ -0,0 +1,12 @@ + + +declare function f() +declare function q() + +function f() +end function + +function q() +end function + + diff --git a/tests/functional/gef16.asm b/tests/functional/gef16.asm index f62c1fee2..54d9384c2 100644 --- a/tests/functional/gef16.asm +++ b/tests/functional/gef16.asm @@ -92,50 +92,52 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti32.asm" - + + #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 3 "lti32.asm" - + __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 @@ -147,24 +149,25 @@ checkParity: ENDP #line 83 "gef16.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 84 "gef16.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/gei32.asm b/tests/functional/gei32.asm index 6d1bc8a5d..948c78e69 100644 --- a/tests/functional/gei32.asm +++ b/tests/functional/gei32.asm @@ -107,50 +107,52 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti32.asm" - + + #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 3 "lti32.asm" - + __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 @@ -162,24 +164,25 @@ checkParity: ENDP #line 98 "gei32.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 99 "gei32.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/gei8.asm b/tests/functional/gei8.asm index cf7a0a31c..7813c9541 100644 --- a/tests/functional/gei8.asm +++ b/tests/functional/gei8.asm @@ -35,6 +35,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -43,10 +44,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -57,7 +58,7 @@ checkParity: ret ENDP #line 26 "gei8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/geu32.asm b/tests/functional/geu32.asm index f4f8e55dc..fac98f9d2 100644 --- a/tests/functional/geu32.asm +++ b/tests/functional/geu32.asm @@ -107,53 +107,55 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 98 "geu32.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 99 "geu32.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/gtf16.asm b/tests/functional/gtf16.asm index e1926a5be..ba15764a1 100644 --- a/tests/functional/gtf16.asm +++ b/tests/functional/gtf16.asm @@ -92,50 +92,52 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lei32.asm" - + + #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 3 "lei32.asm" - + __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 @@ -143,7 +145,7 @@ __LEI32: ; Test 32 bit values Top of the stack <= HL,DE or d ld a, 1 ret z - + ex af, af' jp po, checkParity ld a, d @@ -156,24 +158,25 @@ checkParity: ENDP #line 83 "gtf16.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 84 "gtf16.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/gti32.asm b/tests/functional/gti32.asm index ec7cdd0bc..255406949 100644 --- a/tests/functional/gti32.asm +++ b/tests/functional/gti32.asm @@ -107,50 +107,52 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lei32.asm" - + + #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 3 "lei32.asm" - + __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 @@ -158,7 +160,7 @@ __LEI32: ; Test 32 bit values Top of the stack <= HL,DE or d ld a, 1 ret z - + ex af, af' jp po, checkParity ld a, d @@ -171,24 +173,25 @@ checkParity: ENDP #line 98 "gti32.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 99 "gti32.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/gti8.asm b/tests/functional/gti8.asm index 184445214..83be61bc9 100644 --- a/tests/functional/gti8.asm +++ b/tests/functional/gti8.asm @@ -15,9 +15,8 @@ __START_PROGRAM: call __LTI8 or a jp z, __LABEL1 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL1: ld hl, 0 ld b, h @@ -36,7 +35,9 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti8.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -45,10 +46,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -59,8 +60,8 @@ checkParity: ret ENDP #line 2 "lti8.asm" -#line 27 "gti8.bas" - +#line 26 "gti8.bas" + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/gtu32.asm b/tests/functional/gtu32.asm index dc51fa162..a9ce2ad98 100644 --- a/tests/functional/gtu32.asm +++ b/tests/functional/gtu32.asm @@ -127,24 +127,25 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 118 "gtu32.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/gtu8.asm b/tests/functional/gtu8.asm index cfb34446c..db432d77a 100644 --- a/tests/functional/gtu8.asm +++ b/tests/functional/gtu8.asm @@ -14,9 +14,8 @@ __START_PROGRAM: ld hl, (_a - 1) cp h jp nc, __LABEL1 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL1: ld hl, 0 ld b, h @@ -34,7 +33,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/haplo06.asm b/tests/functional/haplo06.asm new file mode 100644 index 000000000..bd7c4b0e2 --- /dev/null +++ b/tests/functional/haplo06.asm @@ -0,0 +1,52 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, (_y) + ld a, (hl) + ld h, a + ld a, (_a) + or h + push af + ld hl, (_y) + inc hl + ld a, (hl) + ld h, a + pop af + or h + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 +_y: + DEFB 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/haplo06.bas b/tests/functional/haplo06.bas new file mode 100644 index 000000000..0d50974da --- /dev/null +++ b/tests/functional/haplo06.bas @@ -0,0 +1,8 @@ + +DIM a as UByte +DIM y as Uinteger + +a = a bOR peek(y) bOR peek(y + 1) + + + diff --git a/tests/functional/haplo0asm.asm b/tests/functional/haplo0asm.asm new file mode 100644 index 000000000..0580c0c97 --- /dev/null +++ b/tests/functional/haplo0asm.asm @@ -0,0 +1,47 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +#line 0 + tablaColor equ 2 + tablaColorAlto equ tablaColor >> 8 + tablaColorBajo equ tablaColor & 0xFF + tablaColorCheck equ (tablaColorAlto << 8) | tablaColorBajo + tabla1 equ tablaColor + 1 + tabla2 equ tablaColor ^ 2 + tabla3 equ tablaColor % 3 + tabla4 equ tablaColor ~ 5 + ld a, tablaColorAlto + ld b, tablaColorBajo +#line 10 + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/haplo0asm.bas b/tests/functional/haplo0asm.bas new file mode 100644 index 000000000..90393cac9 --- /dev/null +++ b/tests/functional/haplo0asm.bas @@ -0,0 +1,14 @@ +asm +tablaColor equ 2 +tablaColorAlto equ tablaColor >> 8 +tablaColorBajo equ tablaColor & 0xFF +tablaColorCheck equ (tablaColorAlto << 8) | tablaColorBajo +tabla1 equ tablaColor + 1 +tabla2 equ tablaColor ^ 2 +tabla3 equ tablaColor % 3 +tabla4 equ tablaColor ~ 5 + +ld a, tablaColorAlto +ld b, tablaColorBajo +end asm + diff --git a/tests/functional/haplo_out.asm b/tests/functional/haplo_out.asm new file mode 100644 index 000000000..710da8340 --- /dev/null +++ b/tests/functional/haplo_out.asm @@ -0,0 +1,42 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + ld bc, (_b) + out (c), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 +_b: + DEFB 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/haplo_out.bas b/tests/functional/haplo_out.bas new file mode 100644 index 000000000..3f8a56422 --- /dev/null +++ b/tests/functional/haplo_out.bas @@ -0,0 +1,4 @@ +DIM a as UByte +DIM b as Uinteger +OUT b, a + diff --git a/tests/functional/headerless.asm b/tests/functional/headerless.asm new file mode 100644 index 000000000..f1c514445 --- /dev/null +++ b/tests/functional/headerless.asm @@ -0,0 +1,16 @@ + ld hl, _a + inc (hl) + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + ret + +ZXBASIC_USER_DATA: +_a: + DEFB 02h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/headerless.bas b/tests/functional/headerless.bas new file mode 100644 index 000000000..06fe0c957 --- /dev/null +++ b/tests/functional/headerless.bas @@ -0,0 +1,6 @@ +REM a headerless (no prologuqe in ASM) program +#pragma headerless=true + +DIM a as UByte = 2 +LET a = a + 1 + diff --git a/tests/functional/id_substr_eq_expr.asm b/tests/functional/id_substr_eq_expr.asm new file mode 100644 index 000000000..0467871a9 --- /dev/null +++ b/tests/functional/id_substr_eq_expr.asm @@ -0,0 +1,812 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, __LABEL0 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 5 + push hl + ld hl, (_a) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 0005h + DEFB 48h + DEFB 65h + DEFB 6Ch + DEFB 6Ch + DEFB 6Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 36 "id_substr_eq_expr.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 37 "id_substr_eq_expr.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/id_substr_eq_expr.bas b/tests/functional/id_substr_eq_expr.bas new file mode 100644 index 000000000..e5c1aab51 --- /dev/null +++ b/tests/functional/id_substr_eq_expr.bas @@ -0,0 +1,3 @@ + +a$(1 TO 5) = "Hello" + diff --git a/tests/functional/idco.asm b/tests/functional/idco.asm new file mode 100644 index 000000000..988233081 --- /dev/null +++ b/tests/functional/idco.asm @@ -0,0 +1,46 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call _p + call _p + call _p + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_p: + push ix + ld ix, 0 + add ix, sp +_p__leave: + ld sp, ix + pop ix + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/idco.bas b/tests/functional/idco.bas new file mode 100644 index 000000000..22bdea13c --- /dev/null +++ b/tests/functional/idco.bas @@ -0,0 +1,8 @@ + +SUB p() +END sub + +p + +p:p + diff --git a/tests/functional/ifcoendif.asm b/tests/functional/ifcoendif.asm new file mode 100644 index 000000000..b2f9fa4d8 --- /dev/null +++ b/tests/functional/ifcoendif.asm @@ -0,0 +1,72 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 1 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL1 + ld hl, _a + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 26 "ifcoendif.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifcoendif.bas b/tests/functional/ifcoendif.bas new file mode 100644 index 000000000..d3c8a9d17 --- /dev/null +++ b/tests/functional/ifcoendif.bas @@ -0,0 +1,4 @@ +DIM a as Byte + +IF a < 1 THEN: LET a = a + 1: + diff --git a/tests/functional/ifcoendif1.asm b/tests/functional/ifcoendif1.asm new file mode 100644 index 000000000..dcc0e2d27 --- /dev/null +++ b/tests/functional/ifcoendif1.asm @@ -0,0 +1,72 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 1 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL1 + ld hl, _a + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 26 "ifcoendif1.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifcoendif1.bas b/tests/functional/ifcoendif1.bas new file mode 100644 index 000000000..d3c8a9d17 --- /dev/null +++ b/tests/functional/ifcoendif1.bas @@ -0,0 +1,4 @@ +DIM a as Byte + +IF a < 1 THEN: LET a = a + 1: + diff --git a/tests/functional/ifcoendif2.asm b/tests/functional/ifcoendif2.asm new file mode 100644 index 000000000..3767e61c6 --- /dev/null +++ b/tests/functional/ifcoendif2.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + cp 1 + jp nc, __LABEL1 + ld hl, _a + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifcoendif2.bas b/tests/functional/ifcoendif2.bas new file mode 100644 index 000000000..e14670944 --- /dev/null +++ b/tests/functional/ifcoendif2.bas @@ -0,0 +1,4 @@ +DIM a as UByte + +IF a < 1 THEN: LET a = a + 1 + diff --git a/tests/functional/ifcrash.asm b/tests/functional/ifcrash.asm new file mode 100644 index 000000000..89caaf8f5 --- /dev/null +++ b/tests/functional/ifcrash.asm @@ -0,0 +1,532 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_KEYSPACE) + ld de, (_KEYSPACE + 1) + ld bc, (_KEYSPACE + 3) + ld a, c + or l + or h + or e + or d + jp z, __LABEL0 + ld hl, _keyspacepressed + 4 + call __FP_PUSH_REV + ld a, 000h + ld de, 00000h + ld bc, 00000h + call __EQF + or a + jp z, __LABEL3 + ld hl, _nfires + 4 + call __FP_PUSH_REV + ld a, 000h + ld de, 00000h + ld bc, 00000h + call __GTF + push af + ld hl, _fire + 4 + call __FP_PUSH_REV + ld a, 000h + ld de, 00000h + ld bc, 00000h + call __EQF + ld h, a + pop af + or a + jr z, __LABEL6 + ld a, h +__LABEL6: + or a + jp z, __LABEL5 + ld a, (_nsfx) + ld de, (_nsfx + 1) + ld bc, (_nsfx + 3) + call __FTOU32REG + push hl + ld a, 3 + pop hl + ld (hl), a + ld hl, _nfires + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __SUBF + ld hl, _nfires + call __STOREF + ld a, 1 + ld (_a), a + ld a, 081h + ld de, 00000h + ld bc, 00000h + ld hl, _keyspacepressed + call __STOREF +__LABEL5: +__LABEL3: + jp __LABEL1 +__LABEL0: + ld a, 000h + ld de, 00000h + ld bc, 00000h + ld hl, _keyspacepressed + call __STOREF +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "eqf.asm" + +#line 1 "u32tofreg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "u32tofreg.asm" +__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 + +#line 2 "eqf.asm" +#line 1 "ftou32reg.asm" + + + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 3 "eqf.asm" +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 4 "eqf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + + +__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 + +#line 89 "ifcrash.bas" + +#line 1 "gtf.asm" + + + + + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + +__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 + +#line 91 "ifcrash.bas" +#line 1 "pushf.asm" + + + ; 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 + +__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 + + +#line 92 "ifcrash.bas" +#line 1 "storef.asm" + +__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 + +#line 93 "ifcrash.bas" +#line 1 "subf.asm" + + + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + + +__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 + +#line 94 "ifcrash.bas" + +ZXBASIC_USER_DATA: +_KEYSPACE: + DEFB 00, 00, 00, 00, 00 +_keyspacepressed: + DEFB 00, 00, 00, 00, 00 +_nfires: + DEFB 00, 00, 00, 00, 00 +_fire: + DEFB 00, 00, 00, 00, 00 +_nsfx: + DEFB 00, 00, 00, 00, 00 +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifcrash.bas b/tests/functional/ifcrash.bas new file mode 100644 index 000000000..778c65dab --- /dev/null +++ b/tests/functional/ifcrash.bas @@ -0,0 +1,12 @@ +IF KEYSPACE then + if keyspacepressed=0 then + if nfires>0 and fire=0 then + poke nsfx,3 + nfires=nfires-1 + a = 1 + keyspacepressed=1 + end if + end if +else + keyspacepressed=0 +end if diff --git a/tests/functional/ifelse0.asm b/tests/functional/ifelse0.asm index ec7fef5c3..9e4b187ec 100644 --- a/tests/functional/ifelse0.asm +++ b/tests/functional/ifelse0.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/ifelse1.asm b/tests/functional/ifelse1.asm index 80d48f03b..e9ce4c19f 100644 --- a/tests/functional/ifelse1.asm +++ b/tests/functional/ifelse1.asm @@ -16,10 +16,8 @@ __START_PROGRAM: call __PRINT_INIT ld a, (_a) sub 2 - sub 1 - sbc a, a - or a - jp nz, __LABEL1 + jp z, __LABEL1 +__LABEL0: ld hl, __LABEL2 xor a call __PRINTSTR @@ -57,15 +55,17 @@ __LABEL2: DEFB 6Eh DEFB 21h #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -73,45 +73,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -124,41 +125,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -166,12 +169,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -179,50 +183,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -231,15 +236,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -247,26 +254,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -280,44 +287,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -328,27 +336,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -356,27 +365,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -385,81 +395,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -468,32 +480,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -503,7 +515,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -511,19 +523,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -533,7 +546,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -544,18 +557,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -565,7 +579,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -576,18 +590,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -597,7 +612,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -610,21 +625,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -633,79 +649,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -714,75 +730,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -792,7 +808,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -804,16 +820,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -827,49 +843,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -880,17 +896,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -903,27 +919,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -931,10 +947,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -951,80 +967,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1041,8 +1057,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1066,17 +1082,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1087,7 +1103,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1097,21 +1113,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1130,9 +1146,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1157,19 +1173,21 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - -#line 44 "ifelse1.bas" + + +#line 42 "ifelse1.bas" #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1177,25 +1195,25 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1204,40 +1222,41 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1245,25 +1264,25 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1272,39 +1291,39 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; | (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 + ; 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 @@ -1312,56 +1331,56 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1370,57 +1389,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1429,47 +1448,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1479,51 +1498,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1532,11 +1551,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - -#line 45 "ifelse1.bas" - + +#line 43 "ifelse1.bas" + ZXBASIC_USER_DATA: _a: DEFB 02h diff --git a/tests/functional/ifelse2.asm b/tests/functional/ifelse2.asm new file mode 100644 index 000000000..186b9c5c7 --- /dev/null +++ b/tests/functional/ifelse2.asm @@ -0,0 +1,88 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 0 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _i + inc (hl) + jp __LABEL1 +__LABEL0: + ld hl, _i + dec (hl) +__LABEL1: + ld h, 0 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL2 + ld hl, _i + inc (hl) + jp __LABEL3 +__LABEL2: + ld hl, _i + dec (hl) +__LABEL3: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 42 "ifelse2.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifelse2.bas b/tests/functional/ifelse2.bas new file mode 100644 index 000000000..37c371328 --- /dev/null +++ b/tests/functional/ifelse2.bas @@ -0,0 +1,5 @@ +DIM i as Byte + +IF i < 0 THEN LET i = i + 1 ELSE i = i - 1 +IF i < 0 THEN LET i = i + 1 ELSE i = i - 1: END IF + diff --git a/tests/functional/ifelse3.asm b/tests/functional/ifelse3.asm new file mode 100644 index 000000000..d295819a3 --- /dev/null +++ b/tests/functional/ifelse3.asm @@ -0,0 +1,76 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 0 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _i + inc (hl) + jp __LABEL1 +__LABEL0: + ld hl, _i + dec (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 30 "ifelse3.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifelse3.bas b/tests/functional/ifelse3.bas new file mode 100644 index 000000000..ccbe6d4e4 --- /dev/null +++ b/tests/functional/ifelse3.bas @@ -0,0 +1,4 @@ +DIM i as Byte + +IF i < 0 THEN LET i = i + 1 ELSE i = i - 1 + diff --git a/tests/functional/ifelse4.asm b/tests/functional/ifelse4.asm new file mode 100644 index 000000000..3fdf4a49e --- /dev/null +++ b/tests/functional/ifelse4.asm @@ -0,0 +1,76 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 0 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _i + inc (hl) + jp __LABEL1 +__LABEL0: + ld hl, _i + dec (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 30 "ifelse4.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifelse4.bas b/tests/functional/ifelse4.bas new file mode 100644 index 000000000..465a9741f --- /dev/null +++ b/tests/functional/ifelse4.bas @@ -0,0 +1,8 @@ +DIM i as Byte + +IF i < 0 THEN + LET i = i + 1 +ELSE + i = i - 1 +END IF + diff --git a/tests/functional/ifelse5.asm b/tests/functional/ifelse5.asm new file mode 100644 index 000000000..65fb3f74c --- /dev/null +++ b/tests/functional/ifelse5.asm @@ -0,0 +1,84 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 0 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _i + inc (hl) + jp __LABEL1 +__LABEL0: + ld hl, _i + dec (hl) + ld h, 2 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL3 + ld hl, _i + inc (hl) +__LABEL3: +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 38 "ifelse5.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifelse5.bas b/tests/functional/ifelse5.bas new file mode 100644 index 000000000..c867372b6 --- /dev/null +++ b/tests/functional/ifelse5.bas @@ -0,0 +1,4 @@ +DIM i as Byte + +IF i < 0 THEN LET i = i + 1 ELSE i = i - 1: IF i < 2 THEN i = i + 1 + diff --git a/tests/functional/ifempty.asm b/tests/functional/ifempty.asm new file mode 100644 index 000000000..49d4ccd19 --- /dev/null +++ b/tests/functional/ifempty.asm @@ -0,0 +1,37 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifempty.bas b/tests/functional/ifempty.bas new file mode 100644 index 000000000..ebe82d594 --- /dev/null +++ b/tests/functional/ifempty.bas @@ -0,0 +1,6 @@ + +REM for -O1 or higher, this will produce no output +DIM i as Byte + +If 0 i = i + 1 + diff --git a/tests/functional/ifempty0.asm b/tests/functional/ifempty0.asm new file mode 100644 index 000000000..49d4ccd19 --- /dev/null +++ b/tests/functional/ifempty0.asm @@ -0,0 +1,37 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifempty0.bas b/tests/functional/ifempty0.bas new file mode 100644 index 000000000..6c3c067ae --- /dev/null +++ b/tests/functional/ifempty0.bas @@ -0,0 +1,4 @@ +DIM i as Byte + +IF i < 0 THEN : + diff --git a/tests/functional/ifempty1.asm b/tests/functional/ifempty1.asm new file mode 100644 index 000000000..49d4ccd19 --- /dev/null +++ b/tests/functional/ifempty1.asm @@ -0,0 +1,37 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifempty1.bas b/tests/functional/ifempty1.bas new file mode 100644 index 000000000..a3f9b596a --- /dev/null +++ b/tests/functional/ifempty1.bas @@ -0,0 +1,4 @@ +DIM i as Byte + +IF i < 0 THEN : END IF + diff --git a/tests/functional/ifempty2.asm b/tests/functional/ifempty2.asm new file mode 100644 index 000000000..7dca57f38 --- /dev/null +++ b/tests/functional/ifempty2.asm @@ -0,0 +1,73 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 0 + ld a, (_i) + call __LTI8 + or a + jp nz, __LABEL1 +__LABEL0: + ld hl, _i + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 27 "ifempty2.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifempty2.bas b/tests/functional/ifempty2.bas new file mode 100644 index 000000000..20403b4ec --- /dev/null +++ b/tests/functional/ifempty2.bas @@ -0,0 +1,5 @@ +DIM i as Byte + +IF i < 0 THEN : ELSE i = i + 1 + + diff --git a/tests/functional/ifempty3.asm b/tests/functional/ifempty3.asm new file mode 100644 index 000000000..9a28da5ab --- /dev/null +++ b/tests/functional/ifempty3.asm @@ -0,0 +1,76 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 0 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _i + dec (hl) + jp __LABEL1 +__LABEL0: + ld hl, _i + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 30 "ifempty3.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifempty3.bas b/tests/functional/ifempty3.bas new file mode 100644 index 000000000..6d5f3ed11 --- /dev/null +++ b/tests/functional/ifempty3.bas @@ -0,0 +1,5 @@ +DIM i as Byte + +IF i < 0 THEN i = i - 1 ELSE i = i + 1 + + diff --git a/tests/functional/ifempty4.asm b/tests/functional/ifempty4.asm new file mode 100644 index 000000000..49d4ccd19 --- /dev/null +++ b/tests/functional/ifempty4.asm @@ -0,0 +1,37 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifempty4.bas b/tests/functional/ifempty4.bas new file mode 100644 index 000000000..23e7fa0cd --- /dev/null +++ b/tests/functional/ifempty4.bas @@ -0,0 +1,5 @@ + +DIM i as UByte +IF i < 0 THEN +END IF + diff --git a/tests/functional/ifempty5.asm b/tests/functional/ifempty5.asm new file mode 100644 index 000000000..787eec908 --- /dev/null +++ b/tests/functional/ifempty5.asm @@ -0,0 +1,39 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _i + inc (hl) + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifempty5.bas b/tests/functional/ifempty5.bas new file mode 100644 index 000000000..0c9707661 --- /dev/null +++ b/tests/functional/ifempty5.bas @@ -0,0 +1,4 @@ +DIM i as Byte + +IF 1 THEN i = i + 1: END IF + diff --git a/tests/functional/ifempty6.asm b/tests/functional/ifempty6.asm new file mode 100644 index 000000000..698cf25af --- /dev/null +++ b/tests/functional/ifempty6.asm @@ -0,0 +1,39 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _i + dec (hl) + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifempty6.bas b/tests/functional/ifempty6.bas new file mode 100644 index 000000000..a5d238ebd --- /dev/null +++ b/tests/functional/ifempty6.bas @@ -0,0 +1,4 @@ +DIM i as Byte + +IF 0 THEN i = i + 1 ELSE i = i - 1: END IF + diff --git a/tests/functional/ifemptyelse.asm b/tests/functional/ifemptyelse.asm new file mode 100644 index 000000000..463f474fa --- /dev/null +++ b/tests/functional/ifemptyelse.asm @@ -0,0 +1,39 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _a + dec (hl) + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifemptyelse.bas b/tests/functional/ifemptyelse.bas new file mode 100644 index 000000000..0e020e014 --- /dev/null +++ b/tests/functional/ifemptyelse.bas @@ -0,0 +1,5 @@ + +DIM a as Byte + +IF 0 a = a + 1 ELSE a = a - 1 + diff --git a/tests/functional/ifemptylabel1.asm b/tests/functional/ifemptylabel1.asm new file mode 100644 index 000000000..bac7b2391 --- /dev/null +++ b/tests/functional/ifemptylabel1.asm @@ -0,0 +1,40 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + jp __LABEL1 +__LABEL__Here: + ld hl, _a + inc (hl) +__LABEL1: + jp __LABEL__Here +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifemptylabel1.bas b/tests/functional/ifemptylabel1.bas new file mode 100644 index 000000000..cd190e2ec --- /dev/null +++ b/tests/functional/ifemptylabel1.bas @@ -0,0 +1,11 @@ +DIM a as Byte +IF 0 THEN +Here: + a = a + 1 + IF 0 THEN + a = a + 2 + END IF +END IF + +GOTO Here + diff --git a/tests/functional/ifemptylabel2.asm b/tests/functional/ifemptylabel2.asm new file mode 100644 index 000000000..3744bd571 --- /dev/null +++ b/tests/functional/ifemptylabel2.asm @@ -0,0 +1,44 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _a + inc (hl) + jp __LABEL1 +__LABEL0: +__LABEL__Here: + ld a, (_a) + add a, 2 + ld (_a), a +__LABEL1: + jp __LABEL__Here +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifemptylabel2.bas b/tests/functional/ifemptylabel2.bas new file mode 100644 index 000000000..1bc4c4cb6 --- /dev/null +++ b/tests/functional/ifemptylabel2.bas @@ -0,0 +1,13 @@ +DIM a as Byte +IF 1 THEN + a = a + 1 +ELSE +Here: + a = a + 2 + IF 0 THEN + a = a + 3 + END IF +END IF + +GOTO Here + diff --git a/tests/functional/ifemptyprogelse.asm b/tests/functional/ifemptyprogelse.asm new file mode 100644 index 000000000..237c470ee --- /dev/null +++ b/tests/functional/ifemptyprogelse.asm @@ -0,0 +1,37 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifemptyprogelse.bas b/tests/functional/ifemptyprogelse.bas new file mode 100644 index 000000000..740bc0f70 --- /dev/null +++ b/tests/functional/ifemptyprogelse.bas @@ -0,0 +1,8 @@ + +DIM a as Byte + +if a < 0 then +elseif a > 0 then +else +end if + diff --git a/tests/functional/ifendif.bas b/tests/functional/ifendif.bas new file mode 100644 index 000000000..7c2e848e5 --- /dev/null +++ b/tests/functional/ifendif.bas @@ -0,0 +1,6 @@ + +DIM a as Byte + +IF a < 0 THEN: a = a + 1 +END IF + diff --git a/tests/functional/iffor.asm b/tests/functional/iffor.asm new file mode 100644 index 000000000..eba92444b --- /dev/null +++ b/tests/functional/iffor.asm @@ -0,0 +1,84 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL1 + ld a, 1 + ld (_a), a + jp __LABEL2 +__LABEL5: +__LABEL6: + ld hl, _a + inc (hl) +__LABEL2: + ld a, 10 + ld hl, (_a - 1) + call __LTI8 + or a + jp z, __LABEL5 +__LABEL4: +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 38 "iffor.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/iffor.bas b/tests/functional/iffor.bas new file mode 100644 index 000000000..59e21fa96 --- /dev/null +++ b/tests/functional/iffor.bas @@ -0,0 +1,5 @@ + +DIM a as Byte + +IF a < 10 THEN FOR a = 1 TO 10: NEXT a + diff --git a/tests/functional/iffor1.asm b/tests/functional/iffor1.asm new file mode 100644 index 000000000..034ee1f9d --- /dev/null +++ b/tests/functional/iffor1.asm @@ -0,0 +1,84 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL1 + ld a, 1 + ld (_a), a + jp __LABEL2 +__LABEL5: +__LABEL6: + ld hl, _a + inc (hl) +__LABEL2: + ld a, 10 + ld hl, (_a - 1) + call __LTI8 + or a + jp z, __LABEL5 +__LABEL4: +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 38 "iffor1.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/iffor1.bas b/tests/functional/iffor1.bas new file mode 100644 index 000000000..d6cfb7bef --- /dev/null +++ b/tests/functional/iffor1.bas @@ -0,0 +1,6 @@ + +DIM a as Byte + +IF a < 10 THEN FOR a = 1 TO 10 + NEXT a + diff --git a/tests/functional/iffor2.asm b/tests/functional/iffor2.asm new file mode 100644 index 000000000..a92c013bd --- /dev/null +++ b/tests/functional/iffor2.asm @@ -0,0 +1,104 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL1 + ld a, 1 + ld (_a), a + jp __LABEL2 +__LABEL5: + ld a, 1 + ld hl, (_a - 1) + call __LTI8 + or a + jp z, __LABEL8 + ld hl, _a + inc (hl) + jp __LABEL9 +__LABEL12: +__LABEL13: + ld hl, _a + inc (hl) +__LABEL9: + ld a, 10 + ld hl, (_a - 1) + call __LTI8 + or a + jp z, __LABEL12 +__LABEL11: +__LABEL8: +__LABEL6: + ld hl, _a + inc (hl) +__LABEL2: + ld a, 10 + ld hl, (_a - 1) + call __LTI8 + or a + jp z, __LABEL5 +__LABEL4: +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 58 "iffor2.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/iffor2.bas b/tests/functional/iffor2.bas new file mode 100644 index 000000000..b2b5993b4 --- /dev/null +++ b/tests/functional/iffor2.bas @@ -0,0 +1,10 @@ + +DIM a as Byte + +IF a < 10 THEN FOR a = 1 TO 10 + IF a > 1 THEN + FOR a = a + 1 TO 10 + NEXT + END IF + NEXT a + diff --git a/tests/functional/ififelseelse1.asm b/tests/functional/ififelseelse1.asm new file mode 100644 index 000000000..3e6ea45c1 --- /dev/null +++ b/tests/functional/ififelseelse1.asm @@ -0,0 +1,93 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__10: +__LABEL__20: + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _a + inc (hl) + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL2 + ld hl, _a + inc (hl) + jp __LABEL3 +__LABEL2: + ld hl, _a + dec (hl) +__LABEL3: + jp __LABEL1 +__LABEL0: + ld hl, _a + dec (hl) +__LABEL1: +__LABEL__30: + ld hl, _a + inc (hl) + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 47 "ififelseelse1.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ififelseelse1.bas b/tests/functional/ififelseelse1.bas new file mode 100644 index 000000000..5863cdee9 --- /dev/null +++ b/tests/functional/ififelseelse1.bas @@ -0,0 +1,4 @@ +10 DIM a as Byte +20 IF a < 10 THEN LET a = a + 1: IF a < 10 THEN LET a = a + 1: ELSE LET a = a - 1 ELSE: LET a = a - 1 +30 LET a = a + 1 + diff --git a/tests/functional/ififelseelse2.asm b/tests/functional/ififelseelse2.asm new file mode 100644 index 000000000..c8e642efd --- /dev/null +++ b/tests/functional/ififelseelse2.asm @@ -0,0 +1,93 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__10: +__LABEL__20: + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _a + inc (hl) + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL2 + ld hl, _a + inc (hl) + jp __LABEL3 +__LABEL2: + ld hl, _a + dec (hl) +__LABEL3: + jp __LABEL1 +__LABEL0: + ld hl, _a + dec (hl) +__LABEL1: +__LABEL__30: + ld hl, _a + inc (hl) + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 47 "ififelseelse2.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ififelseelse2.bas b/tests/functional/ififelseelse2.bas new file mode 100644 index 000000000..fd07a9a08 --- /dev/null +++ b/tests/functional/ififelseelse2.bas @@ -0,0 +1,4 @@ +10 DIM a as Byte +20 IF a < 10 THEN LET a = a + 1: IF a < 10 THEN LET a = a + 1 ELSE LET a = a - 1 ELSE LET a = a - 1 +30 LET a = a + 1 + diff --git a/tests/functional/ifline.asm b/tests/functional/ifline.asm new file mode 100644 index 000000000..ce56cc573 --- /dev/null +++ b/tests/functional/ifline.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + dec a + jp nz, __LABEL1 + ld hl, _a + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 01h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifline.bas b/tests/functional/ifline.bas new file mode 100644 index 000000000..4506a3c30 --- /dev/null +++ b/tests/functional/ifline.bas @@ -0,0 +1,5 @@ +DIM a as UByte = 1 +IF a = 1 THEN a = a + 1 + + + diff --git a/tests/functional/ifthen.asm b/tests/functional/ifthen.asm index 7ec2148da..6573705de 100644 --- a/tests/functional/ifthen.asm +++ b/tests/functional/ifthen.asm @@ -18,9 +18,8 @@ __LABEL__10: or a jp z, __LABEL1 __LABEL__20: - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL__30: __LABEL1: ld h, 1 @@ -28,9 +27,8 @@ __LABEL1: call __LTI8 or a jp z, __LABEL3 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL3: ld hl, 0 ld b, h @@ -49,7 +47,9 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti8.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -58,10 +58,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -72,8 +72,8 @@ checkParity: ret ENDP #line 2 "lti8.asm" -#line 40 "ifthen.bas" - +#line 38 "ifthen.bas" + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/ifthencoendif.bas b/tests/functional/ifthencoendif.bas new file mode 100644 index 000000000..4ecd06571 --- /dev/null +++ b/tests/functional/ifthencoendif.bas @@ -0,0 +1,7 @@ + +DIM a as Byte + +IF a < 0 THEN: + LET a = a + 1 +END IF + diff --git a/tests/functional/ifthencoendif2.bas b/tests/functional/ifthencoendif2.bas new file mode 100644 index 000000000..584d45a2c --- /dev/null +++ b/tests/functional/ifthencoendif2.bas @@ -0,0 +1,9 @@ + +DIM a as Byte + +IF a < 0 THEN + LET a = a + 1 +ELSE: + LET a = a - 1 +END IF + diff --git a/tests/functional/ifthencosntcoendif.asm b/tests/functional/ifthencosntcoendif.asm new file mode 100644 index 000000000..2759c7538 --- /dev/null +++ b/tests/functional/ifthencosntcoendif.asm @@ -0,0 +1,40 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + jp __LABEL1 +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifthencosntcoendif.bas b/tests/functional/ifthencosntcoendif.bas new file mode 100644 index 000000000..897d57f82 --- /dev/null +++ b/tests/functional/ifthencosntcoendif.bas @@ -0,0 +1,6 @@ + +DIM a as UByte + +IF a < 0 THEN + :LET a = a + 1: END IF + diff --git a/tests/functional/ifthenelse.asm b/tests/functional/ifthenelse.asm index d425548fe..4124e502b 100644 --- a/tests/functional/ifthenelse.asm +++ b/tests/functional/ifthenelse.asm @@ -17,12 +17,11 @@ __LABEL__10: call __LTI8 or a jp z, __LABEL0 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) +__LABEL__20: jp __LABEL1 __LABEL0: -__LABEL__20: xor a ld (_a), a __LABEL__30: @@ -32,9 +31,8 @@ __LABEL1: call __LTI8 or a jp z, __LABEL2 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) jp __LABEL3 __LABEL2: xor a @@ -57,7 +55,9 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti8.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -66,10 +66,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -80,8 +80,8 @@ checkParity: ret ENDP #line 2 "lti8.asm" -#line 48 "ifthenelse.bas" - +#line 46 "ifthenelse.bas" + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/ifthenelseif.asm b/tests/functional/ifthenelseif.asm index b01764853..ed231ff55 100644 --- a/tests/functional/ifthenelseif.asm +++ b/tests/functional/ifthenelseif.asm @@ -17,12 +17,11 @@ __LABEL__10: call __LTI8 or a jp z, __LABEL0 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) +__LABEL__20: jp __LABEL1 __LABEL0: -__LABEL__20: xor a ld hl, (_a - 1) call __LTI8 @@ -39,12 +38,11 @@ __LABEL__40: call __LTI8 or a jp z, __LABEL4 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) +__LABEL__50: jp __LABEL5 __LABEL4: -__LABEL__50: xor a ld hl, (_a - 1) call __LTI8 @@ -68,9 +66,8 @@ __LABEL5: call __LTI8 or a jp z, __LABEL10 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) jp __LABEL11 __LABEL10: xor a @@ -87,9 +84,8 @@ __LABEL11: call __LTI8 or a jp z, __LABEL14 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) jp __LABEL15 __LABEL14: xor a @@ -126,7 +122,9 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti8.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -135,10 +133,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -149,8 +147,8 @@ checkParity: ret ENDP #line 2 "lti8.asm" -#line 117 "ifthenelseif.bas" - +#line 113 "ifthenelseif.bas" + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/ifthenlblsntcoendif.asm b/tests/functional/ifthenlblsntcoendif.asm new file mode 100644 index 000000000..93c1bac35 --- /dev/null +++ b/tests/functional/ifthenlblsntcoendif.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + jp __LABEL1 +__LABEL__10: + ld hl, _a + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifthenlblsntcoendif.bas b/tests/functional/ifthenlblsntcoendif.bas new file mode 100644 index 000000000..341f98d7c --- /dev/null +++ b/tests/functional/ifthenlblsntcoendif.bas @@ -0,0 +1,6 @@ + +DIM a as UByte + +IF a < 0 THEN +10 LET a = a + 1: END IF + diff --git a/tests/functional/ifthensntcoelsecocoendif.asm b/tests/functional/ifthensntcoelsecocoendif.asm new file mode 100644 index 000000000..98958b32d --- /dev/null +++ b/tests/functional/ifthensntcoelsecocoendif.asm @@ -0,0 +1,76 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 1 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _a + inc (hl) + jp __LABEL1 +__LABEL0: + ld hl, _a + dec (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 30 "ifthensntcoelsecocoendif.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifthensntcoelsecocoendif.bas b/tests/functional/ifthensntcoelsecocoendif.bas new file mode 100644 index 000000000..9f0fd22e3 --- /dev/null +++ b/tests/functional/ifthensntcoelsecocoendif.bas @@ -0,0 +1,8 @@ + +DIM a as Byte + +IF a < 1 THEN + LET a = a + 1 +ELSE + :LET a = a - 1: END IF + diff --git a/tests/functional/ifthensntcoelsecoendif.asm b/tests/functional/ifthensntcoelsecoendif.asm new file mode 100644 index 000000000..bca293e2d --- /dev/null +++ b/tests/functional/ifthensntcoelsecoendif.asm @@ -0,0 +1,76 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 1 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL0 + ld hl, _a + inc (hl) + jp __LABEL1 +__LABEL0: + ld hl, _a + dec (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 30 "ifthensntcoelsecoendif.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifthensntcoelsecoendif.bas b/tests/functional/ifthensntcoelsecoendif.bas new file mode 100644 index 000000000..63c83b050 --- /dev/null +++ b/tests/functional/ifthensntcoelsecoendif.bas @@ -0,0 +1,8 @@ + +DIM a as Byte + +IF a < 1 THEN + LET a = a + 1 +ELSE + LET a = a - 1: END IF + diff --git a/tests/functional/ifthensntcoelselblco.asm b/tests/functional/ifthensntcoelselblco.asm new file mode 100644 index 000000000..16eff1818 --- /dev/null +++ b/tests/functional/ifthensntcoelselblco.asm @@ -0,0 +1,48 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + jp __LABEL0 +__LABEL__10: + ld hl, _a + inc (hl) + jp __LABEL1 +__LABEL0: +__LABEL__30: + ld hl, _a + dec (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifthensntcoelselblco.bas b/tests/functional/ifthensntcoelselblco.bas new file mode 100644 index 000000000..3de62008e --- /dev/null +++ b/tests/functional/ifthensntcoelselblco.bas @@ -0,0 +1,8 @@ + +DIM a as UByte + +IF a < 0 THEN +10 LET a = a + 1 +ELSE +30 LET a = a - 1: END IF + diff --git a/tests/functional/ifthensntcoelselblcoco.asm b/tests/functional/ifthensntcoelselblcoco.asm new file mode 100644 index 000000000..16eff1818 --- /dev/null +++ b/tests/functional/ifthensntcoelselblcoco.asm @@ -0,0 +1,48 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + jp __LABEL0 +__LABEL__10: + ld hl, _a + inc (hl) + jp __LABEL1 +__LABEL0: +__LABEL__30: + ld hl, _a + dec (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifthensntcoelselblcoco.bas b/tests/functional/ifthensntcoelselblcoco.bas new file mode 100644 index 000000000..4f5477b18 --- /dev/null +++ b/tests/functional/ifthensntcoelselblcoco.bas @@ -0,0 +1,8 @@ + +DIM a as UByte + +IF a < 0 THEN +10 LET a = a + 1 +ELSE +30 :LET a = a - 1: END IF + diff --git a/tests/functional/ifthensntcoendif.asm b/tests/functional/ifthensntcoendif.asm new file mode 100644 index 000000000..2759c7538 --- /dev/null +++ b/tests/functional/ifthensntcoendif.asm @@ -0,0 +1,40 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + jp __LABEL1 +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifthensntcoendif.bas b/tests/functional/ifthensntcoendif.bas new file mode 100644 index 000000000..56b84f255 --- /dev/null +++ b/tests/functional/ifthensntcoendif.bas @@ -0,0 +1,6 @@ + +DIM a as UByte + +IF a < 0 THEN + LET a = a + 1: END IF + diff --git a/tests/functional/ifwhile.asm b/tests/functional/ifwhile.asm new file mode 100644 index 000000000..6a913e7f7 --- /dev/null +++ b/tests/functional/ifwhile.asm @@ -0,0 +1,80 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 0 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL1 +__LABEL2: + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL3 + ld hl, _a + inc (hl) + jp __LABEL2 +__LABEL3: +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 34 "ifwhile.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifwhile.bas b/tests/functional/ifwhile.bas new file mode 100644 index 000000000..84c8e8247 --- /dev/null +++ b/tests/functional/ifwhile.bas @@ -0,0 +1,6 @@ + +DIM a as Byte = 0 + +IF a < 0 THEN WHILE a < 10: a = a + 1: END WHILE + + diff --git a/tests/functional/ifwhile1.asm b/tests/functional/ifwhile1.asm new file mode 100644 index 000000000..c46da3a09 --- /dev/null +++ b/tests/functional/ifwhile1.asm @@ -0,0 +1,80 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld h, 0 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL1 +__LABEL2: + ld h, 10 + ld a, (_a) + call __LTI8 + or a + jp z, __LABEL3 + ld hl, _a + inc (hl) + jp __LABEL2 +__LABEL3: +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 34 "ifwhile1.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifwhile1.bas b/tests/functional/ifwhile1.bas new file mode 100644 index 000000000..19a556e15 --- /dev/null +++ b/tests/functional/ifwhile1.bas @@ -0,0 +1,7 @@ + +DIM a as Byte = 0 + +IF a < 0 THEN WHILE a < 10 + a = a + 1: END WHILE + + diff --git a/tests/functional/ifwhilex.asm b/tests/functional/ifwhilex.asm new file mode 100644 index 000000000..dc4929b24 --- /dev/null +++ b/tests/functional/ifwhilex.asm @@ -0,0 +1,1374 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __PRINT_INIT + ld h, 0 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL1 +__LABEL2: + ld h, 10 + ld a, (_i) + call __LTI8 + or a + jp z, __LABEL3 + ld hl, _i + inc (hl) + jp __LABEL2 +__LABEL3: + ld a, (_i) + call __PRINTI8 + call PRINT_EOL +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 37 "ifwhilex.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 38 "ifwhilex.bas" +#line 1 "printi8.asm" + +#line 1 "printnum.asm" + + + + +__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 mus preserve registers + ld a, '-' + jp __PRINT_DIGIT + + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + + +#line 2 "printi8.asm" +#line 1 "div8.asm" + + ; -------------------------------- +__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 + +#line 3 "printi8.asm" + +__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 + +#line 39 "ifwhilex.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ifwhilex.bas b/tests/functional/ifwhilex.bas new file mode 100644 index 000000000..ac9fa24d3 --- /dev/null +++ b/tests/functional/ifwhilex.bas @@ -0,0 +1,7 @@ + +DIM i as Byte + +IF i < 0 THEN WHILE i < 10: + LET i = i + 1 + END WHILE: PRINT i + diff --git a/tests/functional/in0.asm b/tests/functional/in0.asm index 5da1dabeb..ffd8f5214 100644 --- a/tests/functional/in0.asm +++ b/tests/functional/in0.asm @@ -29,7 +29,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/inkey.asm b/tests/functional/inkey.asm index 1f8e5c285..5e7fbeacf 100644 --- a/tests/functional/inkey.asm +++ b/tests/functional/inkey.asm @@ -43,15 +43,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "inkey.asm" + ; INKEY Function ; Returns a string allocated in dynamic memory ; containing the string. ; An empty string otherwise. - + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -59,25 +61,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -86,50 +88,51 @@ __CALL_BACK__: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -137,12 +140,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -150,7 +154,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -158,9 +162,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -168,25 +173,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -195,39 +200,39 @@ __STOP: ; | (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 + ; 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 @@ -235,57 +240,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -297,39 +302,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -337,15 +342,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -370,14 +375,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -385,50 +390,50 @@ __MEM_SUBTRACT: 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 - - + + #line 7 "inkey.asm" - + INKEY: - PROC + PROC LOCAL __EMPTY_INKEY LOCAL KEY_SCAN LOCAL KEY_TEST LOCAL KEY_CODE - - ld bc, 3 ; 1 char length string + + 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 @@ -437,7 +442,7 @@ INKEY: dec hl dec hl ; HL Points to string result ret - + __EMPTY_INKEY: pop hl xor a @@ -446,24 +451,26 @@ __EMPTY_INKEY: ld (hl), a dec hl ret - + KEY_SCAN EQU 028Eh KEY_TEST EQU 031Eh KEY_CODE EQU 0333h - + ENDP - + #line 30 "inkey.bas" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -471,45 +478,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -522,65 +530,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -589,15 +599,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -605,26 +617,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -638,44 +650,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -686,27 +699,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -714,27 +728,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -743,81 +758,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -826,32 +843,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -861,7 +878,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -869,19 +886,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -891,7 +909,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -902,18 +920,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -923,7 +942,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -934,18 +953,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -955,7 +975,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -968,21 +988,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -991,79 +1012,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1072,75 +1093,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1150,7 +1171,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1162,16 +1183,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1185,49 +1206,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1238,17 +1259,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1261,27 +1282,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1289,10 +1310,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1309,80 +1330,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1399,8 +1420,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1424,17 +1445,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1445,7 +1466,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1455,21 +1476,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1488,9 +1509,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1515,30 +1536,33 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 31 "inkey.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - - - - + + + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 32 "inkey.bas" #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1546,25 +1570,25 @@ PRINT_EOL_ATTR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1573,38 +1597,38 @@ PRINT_EOL_ATTR: ; | (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 + ; 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 @@ -1613,57 +1637,57 @@ PRINT_EOL_ATTR: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1672,47 +1696,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1722,51 +1746,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1775,52 +1799,53 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 33 "inkey.bas" #line 1 "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. - - - + + + __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 - + #line 34 "inkey.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/inktemp.asm b/tests/functional/inktemp.asm index 897d617ab..1c537c2ef 100644 --- a/tests/functional/inktemp.asm +++ b/tests/functional/inktemp.asm @@ -52,23 +52,25 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "circle.asm" + ; Bresenham's like circle algorithm ; best known as Middle Point Circle drawing algorithm - + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -76,12 +78,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -89,7 +92,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -97,19 +100,22 @@ __STOP: ret #line 5 "circle.asm" #line 1 "plot.asm" + ; 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 - - + + #line 1 "in_screen.asm" + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -117,74 +123,75 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 2 "in_screen.asm" - - + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 9 "plot.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -197,48 +204,48 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 10 "plot.asm" - + 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 - + ld c, h + 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 @@ -246,7 +253,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) res 6, h ; Starts from 0 ld bc, (SCREEN_ADDR) add hl, bc ; Now current offset - + ld b, a inc b ld a, 0FEh @@ -254,7 +261,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) __PLOT_LOOP: rrca djnz __PLOT_LOOP - + ld b, a ld a, (P_FLAG) ld c, a @@ -262,18 +269,18 @@ __PLOT_LOOP: 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 - + ;; gets ATTR position with offset given in SCREEN_ADDR ld a, h rrca @@ -284,36 +291,36 @@ __PLOT_END: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR jp PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + __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 + PIXEL_ADDR EQU 22ACh COORDS EQU 5C7Dh ENDP #line 6 "circle.asm" - - + + ; Draws a circle at X, Y of radius R ; X, Y on the Stack, R in accumulator (Byte) - + PROC LOCAL __CIRCLE_ERROR LOCAL __CIRCLE_LOOP LOCAL __CIRCLE_NEXT - + __CIRCLE_ERROR: jp __OUT_OF_SCREEN_ERR ;; __CIRCLE_ERROR EQU __OUT_OF_SCREEN_ERR ;; __CIRCLE_ERROR: ;; ; Jumps here if out of screen ;; scf ; Always sets carry Flag - ;; + ;; ;; ld a, ERROR_OutOfScreen ;; ld (ERR_NR), a ;; ret @@ -323,95 +330,95 @@ CIRCLE: pop de ; D = Y ex (sp), hl ; __CALLEE__ convention ld e, h ; E = X - - - ld h, a ; H = R + + + ld h, a ; H = R 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 + 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 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 @@ -421,11 +428,11 @@ __CIRCLE_NEXT: ld h, a pop af ex af, af' - - push bc + + push bc exx pop hl ; H'L' = Y, X - + ld a, d add a, h ld b, a ; B = y0 + y @@ -433,7 +440,7 @@ __CIRCLE_NEXT: 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 @@ -441,7 +448,7 @@ __CIRCLE_NEXT: 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 @@ -449,7 +456,7 @@ __CIRCLE_NEXT: 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 @@ -457,61 +464,63 @@ __CIRCLE_NEXT: sub l ld c, a ; C = x0 - x call __CIRCLE_PLOT ; plot(x0 - x, y0 - y) - + ld a, d add a, l ld b, a ; B = y0 + x - ld a, e + 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 + 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 + 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 + ld a, e sub h ld c, a ; C = x0 + y call __CIRCLE_PLOT ; plot(x0 - y, y0 - x) - + exx jp __CIRCLE_LOOP - - - + + + __CIRCLE_PLOT: ; Plots a point of the circle, preserving HL and DE push hl push de - call __PLOT + call __PLOT pop de pop hl ret - + ENDP #line 43 "inktemp.bas" #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -519,35 +528,35 @@ __CIRCLE_PLOT: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 6 "copy_attr.asm" - + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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) - -#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -556,181 +565,186 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 44 "inktemp.bas" #line 1 "draw.asm" + ; 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 - - - - - - + + + + + + #line 1 "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 - -SP.PixelDown: - inc h - ld a,h - and $07 - ret nz - ex af, af' ; Sets carry on F' - scf ; 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 - ret nc - ld a,h - add a,$08 - ld h,a - ;IF DISP_HIRES - ; and $18 - ; cp $18 - ;ELSE - cp $58 - ;ENDIF - ccf - ret + + ; + ; 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 + +SP.PixelDown: + inc h + ld a,h + and $07 + ret nz + ex af, af' ; Sets carry on F' + scf ; 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 + ret nc + ld a,h + add a,$08 + ld h,a + ;IF DISP_HIRES + ; and $18 + ; cp $18 + ;ELSE + cp $58 + ;ENDIF + ccf + ret #line 14 "draw.asm" #line 1 "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 - -SP.PixelUp: - ld a,h - dec h - and $07 - ret nz - ex af, af' - scf - ex af, af' - ld a,$08 - add a,h - ld h,a - ld a,l - sub $20 - ld l,a - ret nc - ld a,h - sub $08 - ld h,a - ;IF DISP_HIRES - ; and $18 - ; cp $18 - ; ccf - ;ELSE - cp $40 - ;ENDIF - ret + + ; + ; 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 + +SP.PixelUp: + ld a,h + dec h + and $07 + ret nz + ex af, af' + scf + ex af, af' + ld a,$08 + add a,h + ld h,a + ld a,l + sub $20 + ld l,a + ret nc + ld a,h + sub $08 + ld h,a + ;IF DISP_HIRES + ; and $18 + ; cp $18 + ; ccf + ;ELSE + cp $40 + ;ENDIF + ret #line 15 "draw.asm" #line 1 "PixelLeft.asm" - ; - ; 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 - - -SP.PixelLeft: - rlca ; Sets new pixel bit 1 to the right - ret nc - 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 - ret - + + ; + ; 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 + + +SP.PixelLeft: + rlca ; Sets new pixel bit 1 to the right + ret nc + 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 + ret + #line 16 "draw.asm" #line 1 "PixelRight.asm" - ; - ; 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 - - -SP.PixelRight: - rrca ; Sets new pixel bit 1 to the right - ret nc - 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 - ret - + + ; + ; 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 + + +SP.PixelRight: + rrca ; Sets new pixel bit 1 to the right + ret nc + 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 + ret + #line 17 "draw.asm" - + ;; DRAW PROCEDURE - PROC - + PROC + LOCAL __DRAW1 LOCAL __DRAW2 LOCAL __DRAW3 @@ -742,22 +756,22 @@ SP.PixelRight: LOCAL __INCX, __INCY, __DECX, __DECY LOCAL P_FLAG P_FLAG EQU 23697 - + __DRAW_ERROR: jp __OUT_OF_SCREEN_ERR - + DRAW: ;; ENTRY POINT - + LOCAL PIXEL_ADDR 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 @@ -765,26 +779,26 @@ DRAW: 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 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 @@ -793,7 +807,7 @@ __DRAW: ld (__PLOTINVERSE), a ld a, 0A6h ; and (hl) jp __DRAW_START - + __DRAW_SETUP1: xor a ; nop ld (__PLOTINVERSE), a @@ -801,7 +815,7 @@ __DRAW_SETUP1: bit 0, c ; Test for OVER jr z, __DRAW_START ld a, 0AEh ; xor (hl) - + __DRAW_START: ld (__PLOTOVER), a ; "Pokes" last operation exx @@ -811,7 +825,7 @@ __DRAW_START: LOCAL __PIXEL_ADDR __PIXEL_ADDR EQU 22ACh call __PIXEL_ADDR - + ;; Now gets pixel mask in A register ld b, a inc b @@ -821,7 +835,7 @@ __DRAW_START: __PIXEL_MASK: rra djnz __PIXEL_MASK - + ld b, d ; Restores B' from D' pop de ; D'E' = y2, x2 exx ; At this point: D'E' = y2,x2 coords @@ -829,147 +843,147 @@ __PIXEL_MASK: ex af, af' ; Saves A reg for later ; A' = Pixel mask ; H'L' = Screen Address of pixel - + ld bc, (COORDS) ; B,C = y1, x1 - - ld a, e + + 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 - + jr z, __DRAW4 ; if error < 0 + exx - add hl, bc ; error += dY + 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 dyncamically updated ld a, e ; Restores A reg. call __FASTPLOT - + __DRAW4_LOOP: ld a, b cp d jp nz, __DRAW3 ld (COORDS), bc - ret - + ret + __DRAW_DX_GT_DY: ; DX > DY ; -------------------------- ; HL = error = dX / 2 ld h, 0 - ld l, c + 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 ; + jr z, __DRAW6 ; exx add hl, bc ; error += dX - exx - + exx + DY2: ; y += yi inc b call __INCY ; This address will be dynamically updated - + __DRAW6: ld a, e DX2: ; x += xi @@ -977,40 +991,40 @@ DX2: ; x += xi 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 - - PIXEL_ADDR EQU 22ACh + + PIXEL_ADDR EQU 22ACh 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: @@ -1018,12 +1032,12 @@ __PLOTINVERSE: __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 ;; gets ATTR position with offset given in SCREEN_ADDR @@ -1036,36 +1050,37 @@ __PLOTOVER: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR call PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + pop de pop hl - - LOCAL __FASTPLOTEND -__FASTPLOTEND: + + LOCAL __FASTPLOTEND +__FASTPLOTEND: or a ; Resets carry flag ex af, af' ; Recovers A reg ld a, e ret - + ENDP - + #line 45 "inktemp.bas" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -1073,37 +1088,38 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 46 "inktemp.bas" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + 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 @@ -1117,41 +1133,42 @@ __SET_INK2: 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 - + #line 47 "inktemp.bas" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register - - - + + + 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 @@ -1161,7 +1178,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1169,39 +1186,40 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 48 "inktemp.bas" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -1212,17 +1230,17 @@ __SET_PAPER2: 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 - + #line 49 "inktemp.bas" - - + + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/jr_no_label.asm b/tests/functional/jr_no_label.asm new file mode 100644 index 000000000..b183aff49 --- /dev/null +++ b/tests/functional/jr_no_label.asm @@ -0,0 +1,2 @@ + jr z,sigPuente + diff --git a/tests/functional/label_sent.asm b/tests/functional/label_sent.asm new file mode 100644 index 000000000..53ece31ca --- /dev/null +++ b/tests/functional/label_sent.asm @@ -0,0 +1,36 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__0: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/label_sent.bas b/tests/functional/label_sent.bas new file mode 100644 index 000000000..c76347c85 --- /dev/null +++ b/tests/functional/label_sent.bas @@ -0,0 +1,2 @@ + 0 REM De MicroHOBBY Nº18, pág. 27 :') + diff --git a/tests/functional/label_sent1.asm b/tests/functional/label_sent1.asm new file mode 100644 index 000000000..3f6e557b4 --- /dev/null +++ b/tests/functional/label_sent1.asm @@ -0,0 +1,49 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__0: +__LABEL__10: + ld a, 1 + call BORDER + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "border.asm" + + ; __FASTCALL__ Routine to change de border + ; Parameter (color) specified in A register + + BORDER EQU 229Bh + + ; Nothing to do! (Directly from the ZX Spectrum ROM) + +#line 22 "label_sent1.bas" + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/label_sent1.bas b/tests/functional/label_sent1.bas new file mode 100644 index 000000000..256b5fff9 --- /dev/null +++ b/tests/functional/label_sent1.bas @@ -0,0 +1,3 @@ +0 REM +10 BORDER 1 + diff --git a/tests/functional/label_sent2.asm b/tests/functional/label_sent2.asm new file mode 100644 index 000000000..d44a692bd --- /dev/null +++ b/tests/functional/label_sent2.asm @@ -0,0 +1,157 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__0: +__LABEL__10: + ld a, 1 + call BORDER + ld a, 1 + call PAPER + call COPY_ATTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "border.asm" + + ; __FASTCALL__ Routine to change de border + ; Parameter (color) specified in A register + + BORDER EQU 229Bh + + ; Nothing to do! (Directly from the ZX Spectrum ROM) + +#line 25 "label_sent2.bas" +#line 1 "copy_attr.asm" + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 6 "copy_attr.asm" + +COPY_ATTR: + ; Just copies current permanent attribs to 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) + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" + ret +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 26 "label_sent2.bas" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 27 "label_sent2.bas" + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/label_sent2.bas b/tests/functional/label_sent2.bas new file mode 100644 index 000000000..201640dd4 --- /dev/null +++ b/tests/functional/label_sent2.bas @@ -0,0 +1,3 @@ +0 REM +10 BORDER 1: PAPER 1 + diff --git a/tests/functional/label_sent3.asm b/tests/functional/label_sent3.asm new file mode 100644 index 000000000..1793698f7 --- /dev/null +++ b/tests/functional/label_sent3.asm @@ -0,0 +1,157 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__0: +__LABEL__10: + ld a, 1 + call BORDER + ld a, 1 + call PAPER + call COPY_ATTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "border.asm" + + ; __FASTCALL__ Routine to change de border + ; Parameter (color) specified in A register + + BORDER EQU 229Bh + + ; Nothing to do! (Directly from the ZX Spectrum ROM) + +#line 25 "label_sent3.bas" +#line 1 "copy_attr.asm" + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 6 "copy_attr.asm" + +COPY_ATTR: + ; Just copies current permanent attribs to 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) + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" + ret +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 26 "label_sent3.bas" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 27 "label_sent3.bas" + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/label_sent3.bas b/tests/functional/label_sent3.bas new file mode 100644 index 000000000..7ec6c78f7 --- /dev/null +++ b/tests/functional/label_sent3.bas @@ -0,0 +1,3 @@ +0 REM +10 BORDER 1: PAPER 1: + diff --git a/tests/functional/label_sent4.asm b/tests/functional/label_sent4.asm new file mode 100644 index 000000000..022387cbb --- /dev/null +++ b/tests/functional/label_sent4.asm @@ -0,0 +1,157 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__0: +__LABEL__10: + ld a, 1 + call BORDER + ld a, 1 + call PAPER + call COPY_ATTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "border.asm" + + ; __FASTCALL__ Routine to change de border + ; Parameter (color) specified in A register + + BORDER EQU 229Bh + + ; Nothing to do! (Directly from the ZX Spectrum ROM) + +#line 25 "label_sent4.bas" +#line 1 "copy_attr.asm" + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 6 "copy_attr.asm" + +COPY_ATTR: + ; Just copies current permanent attribs to 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) + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" + ret +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 26 "label_sent4.bas" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 27 "label_sent4.bas" + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/label_sent4.bas b/tests/functional/label_sent4.bas new file mode 100644 index 000000000..67cfae530 --- /dev/null +++ b/tests/functional/label_sent4.bas @@ -0,0 +1,3 @@ +0 REM +10 :BORDER 1: PAPER 1 + diff --git a/tests/functional/label_sent5.asm b/tests/functional/label_sent5.asm new file mode 100644 index 000000000..474f9b417 --- /dev/null +++ b/tests/functional/label_sent5.asm @@ -0,0 +1,157 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__0: +__LABEL__10: + ld a, 1 + call BORDER + ld a, 1 + call PAPER + call COPY_ATTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "border.asm" + + ; __FASTCALL__ Routine to change de border + ; Parameter (color) specified in A register + + BORDER EQU 229Bh + + ; Nothing to do! (Directly from the ZX Spectrum ROM) + +#line 25 "label_sent5.bas" +#line 1 "copy_attr.asm" + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 6 "copy_attr.asm" + +COPY_ATTR: + ; Just copies current permanent attribs to 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) + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" + ret +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 26 "label_sent5.bas" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 27 "label_sent5.bas" + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/label_sent5.bas b/tests/functional/label_sent5.bas new file mode 100644 index 000000000..e7a7651b9 --- /dev/null +++ b/tests/functional/label_sent5.bas @@ -0,0 +1,3 @@ +0 REM +10 :BORDER 1: PAPER 1: + diff --git a/tests/functional/labeldecl.asm b/tests/functional/labeldecl.asm new file mode 100644 index 000000000..a21ec6f74 --- /dev/null +++ b/tests/functional/labeldecl.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__10: +__LABEL__20: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 +_b: + DEFB 00 +_c: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/labeldecl.bas b/tests/functional/labeldecl.bas new file mode 100644 index 000000000..fda6d4718 --- /dev/null +++ b/tests/functional/labeldecl.bas @@ -0,0 +1,5 @@ + + +10 DIM a as Byte +20 DIM b as Byte : DIM c as Byte + diff --git a/tests/functional/labelsent.asm b/tests/functional/labelsent.asm new file mode 100644 index 000000000..4b7bc942e --- /dev/null +++ b/tests/functional/labelsent.asm @@ -0,0 +1,189 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__10: + ld hl, _a + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __ADDF + ld hl, _a + call __STOREF + ld hl, _b + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __ADDF + ld hl, _b + call __STOREF + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "addf.asm" + +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 2 "addf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + +__ADDF: ; Addition + call __FPSTACK_PUSH2 + + ; ------------- ROM ADD + rst 28h + defb 0fh ; ADD + defb 38h; ; END CALC + + jp __FPSTACK_POP + +#line 35 "labelsent.bas" +#line 1 "pushf.asm" + + + ; 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 + +__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 + + +#line 36 "labelsent.bas" +#line 1 "storef.asm" + +__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 + +#line 37 "labelsent.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00, 00, 00, 00 +_b: + DEFB 00, 00, 00, 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/labelsent.bas b/tests/functional/labelsent.bas new file mode 100644 index 000000000..9583ac06c --- /dev/null +++ b/tests/functional/labelsent.bas @@ -0,0 +1,4 @@ + + +10 LET a = a + 1: LET b = b + 1 + diff --git a/tests/functional/lcd3.asm b/tests/functional/lcd3.asm index 24c3b79ff..e22716e4b 100644 --- a/tests/functional/lcd3.asm +++ b/tests/functional/lcd3.asm @@ -154,38 +154,40 @@ __LABEL2: DEFB 4Fh DEFB 46h #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -201,7 +203,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -210,22 +212,23 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 141 "lcd3.bas" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -233,25 +236,25 @@ __ADDF: ; Addition ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -260,40 +263,41 @@ __ADDF: ; Addition ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -301,25 +305,25 @@ __ADDF: ; Addition ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -328,39 +332,39 @@ __ADDF: ; Addition ; | (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 + ; 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 @@ -368,56 +372,56 @@ __ADDF: ; Addition __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -426,57 +430,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -485,47 +489,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -535,55 +539,58 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 142 "lcd3.bas" #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -591,68 +598,86 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 143 "lcd3.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -660,25 +685,25 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -687,50 +712,51 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -738,12 +764,13 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -751,16 +778,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -772,39 +799,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/src/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/src/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 @@ -812,15 +839,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -845,14 +872,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -860,25 +887,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -887,30 +914,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -918,15 +945,17 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 144 "lcd3.bas" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -934,45 +963,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -985,65 +1015,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -1052,15 +1084,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -1068,26 +1102,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -1101,44 +1135,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -1149,27 +1184,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -1177,27 +1213,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -1206,81 +1243,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1289,32 +1328,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1324,7 +1363,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1332,19 +1371,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1354,7 +1394,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1365,18 +1405,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1386,7 +1427,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1397,18 +1438,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1418,7 +1460,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1431,21 +1473,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1454,79 +1497,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1535,75 +1578,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1613,7 +1656,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1625,16 +1668,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1648,49 +1691,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1701,17 +1744,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1724,27 +1767,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1752,10 +1795,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1772,80 +1815,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1862,8 +1905,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1887,17 +1930,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1908,7 +1951,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1918,21 +1961,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1951,9 +1994,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1978,57 +2021,58 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 145 "lcd3.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -2037,74 +2081,77 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 146 "lcd3.bas" #line 1 "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 "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. - - - + + + __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 - + #line 9 "pstorestr2.asm" - + __PSTORE_STR2: push ix pop hl add hl, bc jp __STORE_STR2 - + #line 147 "lcd3.bas" #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -2125,183 +2172,186 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - + + #line 148 "lcd3.bas" #line 1 "str.asm" + ; 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 - - - - - + + + + + __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 + 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 - + #line 149 "lcd3.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -2309,52 +2359,53 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 150 "lcd3.bas" - + #line 1 "u32tofreg.asm" - + + __I8TOFREG: ld l, a rlca @@ -2362,35 +2413,35 @@ __I8TOFREG: 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 @@ -2398,20 +2449,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -2419,32 +2470,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 152 "lcd3.bas" - + ZXBASIC_USER_DATA: _adr: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/lcd5.asm b/tests/functional/lcd5.asm index d5c588e76..7ecb9465b 100644 --- a/tests/functional/lcd5.asm +++ b/tests/functional/lcd5.asm @@ -43,7 +43,7 @@ _SubLifetime__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: _tile: DEFW 0000h diff --git a/tests/functional/lcd6.asm b/tests/functional/lcd6.asm new file mode 100644 index 000000000..247dc1ede --- /dev/null +++ b/tests/functional/lcd6.asm @@ -0,0 +1,109 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_SubLifetime: + push ix + ld ix, 0 + add ix, sp + ld hl, -21 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 20 + ld d, h + ld e, l + inc de + ldir + push ix + pop hl + ld bc, -21 + add hl, bc + ex de, hl + ld hl, __LABEL0 + ld bc, 3 + ldir +_SubLifetime__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + ex (sp), hl + exx + ret + +ZXBASIC_USER_DATA: +_tile: + DEFW 0000h + DEFB 02h + 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 + 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 +__LABEL0: + DEFB 00h + DEFB 00h + DEFB 01h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/lcd6.bas b/tests/functional/lcd6.bas new file mode 100644 index 000000000..177f68553 --- /dev/null +++ b/tests/functional/lcd6.bas @@ -0,0 +1,5 @@ +dim tile(17) as Uinteger +sub SubLifetime(x as integer,y as integer,value as byte) + dim tile(17) as ubyte +end sub + diff --git a/tests/functional/lcd7.asm b/tests/functional/lcd7.asm index 36c1441a7..782b3e192 100644 --- a/tests/functional/lcd7.asm +++ b/tests/functional/lcd7.asm @@ -85,9 +85,10 @@ __LABEL0: DEFB 4Fh DEFB 4Bh #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -95,25 +96,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -122,40 +123,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -163,25 +165,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -190,39 +192,39 @@ __LABEL0: ; | (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 + ; 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 @@ -230,56 +232,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -288,57 +290,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -347,47 +349,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -397,15 +399,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "lcd7.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -413,25 +417,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -440,50 +444,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -491,12 +496,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -504,16 +510,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -525,39 +531,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -565,15 +571,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -598,14 +604,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -613,25 +619,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -640,30 +646,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -671,19 +677,22 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 73 "lcd7.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -691,45 +700,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -742,65 +752,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -809,15 +821,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -825,26 +839,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -858,44 +872,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -906,27 +921,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -934,27 +950,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -963,81 +980,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1046,32 +1065,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1081,7 +1100,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1089,19 +1108,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1111,7 +1131,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1122,18 +1142,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1143,7 +1164,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1154,18 +1175,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1175,7 +1197,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1188,21 +1210,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1211,79 +1234,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1292,75 +1315,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1370,7 +1393,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1382,16 +1405,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1405,49 +1428,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1458,17 +1481,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1481,27 +1504,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1509,10 +1532,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1529,80 +1552,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1619,8 +1642,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1644,17 +1667,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1665,7 +1688,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1675,21 +1698,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1708,9 +1731,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1735,63 +1758,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 74 "lcd7.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1800,18 +1824,20 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 75 "lcd7.bas" #line 1 "pstorestr.asm" + ; vim:ts=4:et:sw=4 - ; + ; ; 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 ; - + #line 1 "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$) @@ -1822,13 +1848,15 @@ __PRINT_STR: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1836,25 +1864,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1863,42 +1891,42 @@ __PRINT_STR: ; | (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 + ; 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. @@ -1917,29 +1945,29 @@ __PRINT_STR: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -1948,111 +1976,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -2068,7 +2096,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -2076,52 +2104,52 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 8 "pstorestr.asm" - + __PSTORE_STR: push ix pop hl add hl, bc jp __STORE_STR - + #line 76 "lcd7.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/lcd8.asm b/tests/functional/lcd8.asm index dfde6ad6b..d1397334c 100644 --- a/tests/functional/lcd8.asm +++ b/tests/functional/lcd8.asm @@ -87,9 +87,10 @@ __LABEL0: DEFB 4Fh DEFB 4Bh #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -97,25 +98,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -124,40 +125,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -165,25 +167,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -192,39 +194,39 @@ __LABEL0: ; | (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 + ; 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 @@ -232,56 +234,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -290,57 +292,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -349,47 +351,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -399,15 +401,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 74 "lcd8.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -415,25 +419,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -442,50 +446,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -493,12 +498,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -506,16 +512,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -527,39 +533,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -567,15 +573,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -600,14 +606,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -615,25 +621,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -642,30 +648,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -673,19 +679,22 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 75 "lcd8.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -693,45 +702,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -744,65 +754,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -811,15 +823,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -827,26 +841,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -860,44 +874,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -908,27 +923,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -936,27 +952,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -965,81 +982,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1048,32 +1067,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1083,7 +1102,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1091,19 +1110,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1113,7 +1133,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1124,18 +1144,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1145,7 +1166,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1156,18 +1177,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1177,7 +1199,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1190,21 +1212,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1213,79 +1236,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1294,75 +1317,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1372,7 +1395,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1384,16 +1407,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1407,49 +1430,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1460,17 +1483,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1483,27 +1506,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1511,10 +1534,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1531,80 +1554,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1621,8 +1644,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1646,17 +1669,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1667,7 +1690,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1677,21 +1700,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1710,9 +1733,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1737,63 +1760,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 76 "lcd8.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1802,11 +1826,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 77 "lcd8.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/lcd9.asm b/tests/functional/lcd9.asm index 2ec1ca09f..4c609fbaa 100644 --- a/tests/functional/lcd9.asm +++ b/tests/functional/lcd9.asm @@ -75,9 +75,10 @@ __LABEL0: DEFB 4Fh DEFB 4Bh #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -85,25 +86,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -112,40 +113,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -153,25 +155,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -180,39 +182,39 @@ __LABEL0: ; | (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 + ; 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 @@ -220,56 +222,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -278,57 +280,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -337,47 +339,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -387,15 +389,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 62 "lcd9.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -403,25 +407,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -430,50 +434,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -481,12 +486,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -494,16 +500,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -515,39 +521,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -555,15 +561,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -588,14 +594,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -603,25 +609,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -630,30 +636,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -661,19 +667,22 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 63 "lcd9.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -681,45 +690,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -732,65 +742,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -799,15 +811,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -815,26 +829,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -848,44 +862,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -896,27 +911,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -924,27 +940,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -953,81 +970,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1036,32 +1055,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1071,7 +1090,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1079,19 +1098,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1101,7 +1121,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1112,18 +1132,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1133,7 +1154,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1144,18 +1165,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1165,7 +1187,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1178,21 +1200,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1201,79 +1224,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1282,75 +1305,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1360,7 +1383,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1372,16 +1395,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1395,49 +1418,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1448,17 +1471,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1471,27 +1494,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1499,10 +1522,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1519,80 +1542,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1609,8 +1632,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1634,17 +1657,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1655,7 +1678,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1665,21 +1688,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1698,9 +1721,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1725,63 +1748,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 64 "lcd9.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1790,11 +1814,12 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 65 "lcd9.bas" #line 1 "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$) @@ -1805,13 +1830,15 @@ __PRINT_STR: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1819,25 +1846,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1846,42 +1873,42 @@ __PRINT_STR: ; | (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 + ; 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. @@ -1900,29 +1927,29 @@ __PRINT_STR: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -1931,111 +1958,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -2051,7 +2078,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -2059,44 +2086,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 66 "lcd9.bas" - + ZXBASIC_USER_DATA: _strg: DEFB 00, 00 diff --git a/tests/functional/lcd_crash.asm b/tests/functional/lcd_crash.asm new file mode 100644 index 000000000..f27044d00 --- /dev/null +++ b/tests/functional/lcd_crash.asm @@ -0,0 +1,192 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, __LABEL__void + ld (_tiles + 3), hl + ld (_tiles + 5), hl +__LABEL__void: + xor a + push af + xor a + push af + xor a + push af + call _settile + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_putChars: + push ix + ld ix, 0 + add ix, sp +_putChars__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_settile: + push ix + ld ix, 0 + add ix, sp + ld hl, __LABEL__void + push hl + ld a, 3 + push af + ld a, 3 + push af + ld a, (_monsterx) + ld h, 3 + call __MUL8_FAST + ld l, a + add a, a + sbc a, a + ld h, a + push hl + ld a, (_monsterx) + ld h, 3 + call __MUL8_FAST + ld l, a + add a, a + sbc a, a + ld h, a + push hl + call _putChars +_settile__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + ex (sp), hl + exx + ret +#line 1 "mul8.asm" + +__MUL8: ; Performs 8bit x 8bit multiplication + PROC + + ;LOCAL __MUL8A + LOCAL __MUL8LOOP + LOCAL __MUL8B + ; 1st operand (byte) in A, 2nd operand into the stack (AF) + pop hl ; return address + ex (sp), hl ; CALLE convention + +;;__MUL8_FAST: ; __FASTCALL__ entry + ;; ld e, a + ;; ld d, 0 + ;; ld l, d + ;; + ;; sla h + ;; jr nc, __MUL8A + ;; ld l, e + ;; +;;__MUL8A: + ;; + ;; ld b, 7 +;;__MUL8LOOP: + ;; add hl, hl + ;; jr nc, __MUL8B + ;; + ;; add hl, de + ;; +;;__MUL8B: + ;; djnz __MUL8LOOP + ;; + ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) + +__MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry + + ld b, 8 + ld l, a + xor a + +__MUL8LOOP: + add a, a ; a *= 2 + sla l + jp nc, __MUL8B + add a, h + +__MUL8B: + djnz __MUL8LOOP + + ret ; result = HL + ENDP + +#line 82 "lcd_crash.bas" + +ZXBASIC_USER_DATA: +_monsterx: + DEFB 00 +_tiles: + DEFW 0000h + DEFB 02h + 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 + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/lcd_crash.bas b/tests/functional/lcd_crash.bas new file mode 100644 index 000000000..6e6adc875 --- /dev/null +++ b/tests/functional/lcd_crash.bas @@ -0,0 +1,14 @@ +dim tiles(16) as uinteger +tiles(0)=@void +tiles(1)=tiles(0) + +dim monsterx as byte +void: + +sub putChars(x as Uinteger, y as Uinteger, a as Ubyte, b as Ubyte, addr as Uinteger) +end Sub + +sub settile(x as ubyte,y as ubyte,tile as ubyte) + putChars(monsterx*3,monsterx*3,3,3,@void) +end sub +settile(0, 0, 0) diff --git a/tests/functional/lef16.asm b/tests/functional/lef16.asm index a532a0a3e..b5db14f45 100644 --- a/tests/functional/lef16.asm +++ b/tests/functional/lef16.asm @@ -84,50 +84,52 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lei32.asm" - + + #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 3 "lei32.asm" - + __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 @@ -135,7 +137,7 @@ __LEI32: ; Test 32 bit values Top of the stack <= HL,DE or d ld a, 1 ret z - + ex af, af' jp po, checkParity ld a, d @@ -148,24 +150,25 @@ checkParity: ENDP #line 75 "lef16.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 76 "lef16.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/lei32.asm b/tests/functional/lei32.asm index a6efbc0be..9ea97dc0b 100644 --- a/tests/functional/lei32.asm +++ b/tests/functional/lei32.asm @@ -97,50 +97,52 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lei32.asm" - + + #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 3 "lei32.asm" - + __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 @@ -148,7 +150,7 @@ __LEI32: ; Test 32 bit values Top of the stack <= HL,DE or d ld a, 1 ret z - + ex af, af' jp po, checkParity ld a, d @@ -161,24 +163,25 @@ checkParity: ENDP #line 88 "lei32.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 89 "lei32.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/let0.asm b/tests/functional/let0.asm index 1616a05db..dccad562a 100644 --- a/tests/functional/let0.asm +++ b/tests/functional/let0.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 01h diff --git a/tests/functional/let_array_substr.asm b/tests/functional/let_array_substr.asm new file mode 100644 index 000000000..8e3fdb996 --- /dev/null +++ b/tests/functional/let_array_substr.asm @@ -0,0 +1,1368 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, (_b) + push hl + ld hl, _a + call __ARRAY + ld de, __LABEL0 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 5 + push hl + ld hl, (_b) + push hl + ld hl, _a + call __ARRAY + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/src/zxb/trunk/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 61 "let_array_substr.bas" +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 62 "let_array_substr.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 63 "let_array_substr.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 64 "let_array_substr.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 03h + DEFB 00h +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr.bas b/tests/functional/let_array_substr.bas new file mode 100644 index 000000000..7258295b6 --- /dev/null +++ b/tests/functional/let_array_substr.bas @@ -0,0 +1,5 @@ +DIM a$(10) +DIM b as UInteger = 3 +LET a$(b) = "0123456789" +LET a$(b)(1 TO 5) = "HELLO" + diff --git a/tests/functional/let_array_substr1.asm b/tests/functional/let_array_substr1.asm new file mode 100644 index 000000000..a8df9e06d --- /dev/null +++ b/tests/functional/let_array_substr1.asm @@ -0,0 +1,1368 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, (_b) + push hl + ld hl, _a + call __ARRAY + ld de, __LABEL0 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 5 + push hl + ld hl, (_b) + push hl + ld hl, _a + call __ARRAY + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/src/zxb/trunk/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 61 "let_array_substr1.bas" +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 62 "let_array_substr1.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 63 "let_array_substr1.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 64 "let_array_substr1.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 03h + DEFB 00h +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr1.bas b/tests/functional/let_array_substr1.bas new file mode 100644 index 000000000..ae5f2f6e0 --- /dev/null +++ b/tests/functional/let_array_substr1.bas @@ -0,0 +1,5 @@ +DIM a$(10) +DIM b as UInteger = 3 +LET a$(b) = "0123456789" +a$(b)(1 TO 5) = "HELLO" + diff --git a/tests/functional/let_array_substr10.asm b/tests/functional/let_array_substr10.asm new file mode 100644 index 000000000..385dfdd1c --- /dev/null +++ b/tests/functional/let_array_substr10.asm @@ -0,0 +1,1155 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + 11 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 0 + push hl + ld hl, 3 + push hl + ld hl, (_a + 11) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 51 "let_array_substr10.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 52 "let_array_substr10.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 53 "let_array_substr10.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr10.bas b/tests/functional/let_array_substr10.bas new file mode 100644 index 000000000..38123f081 --- /dev/null +++ b/tests/functional/let_array_substr10.bas @@ -0,0 +1,4 @@ +DIM a$(10) +LET a(4) = "0123456789" +LET a(4, TO 3) = "HELLO" + diff --git a/tests/functional/let_array_substr11.asm b/tests/functional/let_array_substr11.asm new file mode 100644 index 000000000..bff978cf8 --- /dev/null +++ b/tests/functional/let_array_substr11.asm @@ -0,0 +1,1155 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + 9 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 0 + push hl + ld hl, 65534 + push hl + ld hl, (_a + 9) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 51 "let_array_substr11.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 52 "let_array_substr11.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 53 "let_array_substr11.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr11.bas b/tests/functional/let_array_substr11.bas new file mode 100644 index 000000000..ba8cec2ca --- /dev/null +++ b/tests/functional/let_array_substr11.bas @@ -0,0 +1,4 @@ +DIM a$(10) +LET a(3) = "0123456789" +LET a(3, TO) = "HELLO" + diff --git a/tests/functional/let_array_substr12.asm b/tests/functional/let_array_substr12.asm new file mode 100644 index 000000000..7a02d0a5e --- /dev/null +++ b/tests/functional/let_array_substr12.asm @@ -0,0 +1,1155 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + 9 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 5 + push hl + ld hl, 5 + push hl + ld hl, (_a + 9) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 51 "let_array_substr12.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 52 "let_array_substr12.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 53 "let_array_substr12.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr12.bas b/tests/functional/let_array_substr12.bas new file mode 100644 index 000000000..03bd92466 --- /dev/null +++ b/tests/functional/let_array_substr12.bas @@ -0,0 +1,4 @@ +DIM a$(10) +LET a(3) = "0123456789" +LET a(3, 5 TO 5) = "HELLO" + diff --git a/tests/functional/let_array_substr13.asm b/tests/functional/let_array_substr13.asm new file mode 100644 index 000000000..cc97b2105 --- /dev/null +++ b/tests/functional/let_array_substr13.asm @@ -0,0 +1,1155 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + 9 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 5 + push hl + ld hl, 5 + push hl + ld hl, (_a + 9) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 51 "let_array_substr13.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 52 "let_array_substr13.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 53 "let_array_substr13.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr13.bas b/tests/functional/let_array_substr13.bas new file mode 100644 index 000000000..bd5a50ffa --- /dev/null +++ b/tests/functional/let_array_substr13.bas @@ -0,0 +1,4 @@ +DIM a$(10) +LET a(3) = "0123456789" +LET a(3, 5) = "HELLO" + diff --git a/tests/functional/let_array_substr2.asm b/tests/functional/let_array_substr2.asm new file mode 100644 index 000000000..7e8e0221b --- /dev/null +++ b/tests/functional/let_array_substr2.asm @@ -0,0 +1,1155 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + 9 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 5 + push hl + ld hl, (_a + 9) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 51 "let_array_substr2.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 52 "let_array_substr2.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 53 "let_array_substr2.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr2.bas b/tests/functional/let_array_substr2.bas new file mode 100644 index 000000000..658c8dab6 --- /dev/null +++ b/tests/functional/let_array_substr2.bas @@ -0,0 +1,4 @@ +DIM a$(10) +LET a$(3) = "0123456789" +LET a$(3)(1 TO 5) = "HELLO" + diff --git a/tests/functional/let_array_substr3.asm b/tests/functional/let_array_substr3.asm new file mode 100644 index 000000000..7dbc87a87 --- /dev/null +++ b/tests/functional/let_array_substr3.asm @@ -0,0 +1,1155 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + 9 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 5 + push hl + ld hl, (_a + 9) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 51 "let_array_substr3.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 52 "let_array_substr3.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 53 "let_array_substr3.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr3.bas b/tests/functional/let_array_substr3.bas new file mode 100644 index 000000000..b806f81f8 --- /dev/null +++ b/tests/functional/let_array_substr3.bas @@ -0,0 +1,4 @@ +DIM a$(10) +LET a$(3) = "0123456789" +a$(3)(1 TO 5) = "HELLO" + diff --git a/tests/functional/let_array_substr4.bas b/tests/functional/let_array_substr4.bas new file mode 100644 index 000000000..0c102974c --- /dev/null +++ b/tests/functional/let_array_substr4.bas @@ -0,0 +1,3 @@ +DIM a(10) as UInteger +LET a(3)(1 TO 5) = 4 + diff --git a/tests/functional/let_array_substr5.asm b/tests/functional/let_array_substr5.asm new file mode 100644 index 000000000..a3a06a424 --- /dev/null +++ b/tests/functional/let_array_substr5.asm @@ -0,0 +1,1368 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, (_b) + push hl + ld hl, _a + call __ARRAY + ld de, __LABEL0 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 1 + push hl + ld hl, (_b) + push hl + ld hl, _a + call __ARRAY + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/src/zxb/trunk/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 61 "let_array_substr5.bas" +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 62 "let_array_substr5.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 63 "let_array_substr5.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 64 "let_array_substr5.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 03h + DEFB 00h +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr5.bas b/tests/functional/let_array_substr5.bas new file mode 100644 index 000000000..9f18b698f --- /dev/null +++ b/tests/functional/let_array_substr5.bas @@ -0,0 +1,5 @@ +DIM a$(10) +DIM b as UInteger = 3 +LET a$(b) = "0123456789" +a$(b)(1) = "HELLO" + diff --git a/tests/functional/let_array_substr6.bas b/tests/functional/let_array_substr6.bas new file mode 100644 index 000000000..f0b849197 --- /dev/null +++ b/tests/functional/let_array_substr6.bas @@ -0,0 +1,3 @@ +DIM a(10) as UInteger +LET a(3)(5) = 4 + diff --git a/tests/functional/let_array_substr7.asm b/tests/functional/let_array_substr7.asm new file mode 100644 index 000000000..a9d9b8ba7 --- /dev/null +++ b/tests/functional/let_array_substr7.asm @@ -0,0 +1,1155 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + 9 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 1 + push hl + ld hl, (_a + 9) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 51 "let_array_substr7.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 52 "let_array_substr7.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 53 "let_array_substr7.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr7.bas b/tests/functional/let_array_substr7.bas new file mode 100644 index 000000000..2f800a0e2 --- /dev/null +++ b/tests/functional/let_array_substr7.bas @@ -0,0 +1,4 @@ +DIM a$(10) +LET a$(3) = "0123456789" +a$(3)(1) = "HELLO" + diff --git a/tests/functional/let_array_substr8.bas b/tests/functional/let_array_substr8.bas new file mode 100644 index 000000000..6a9a48047 --- /dev/null +++ b/tests/functional/let_array_substr8.bas @@ -0,0 +1,5 @@ +DIM a$(10) +LET a(3) = "0123456789" +LET a(3, 4)(1) = "HELLO" +print a$(3) + diff --git a/tests/functional/let_array_substr9.asm b/tests/functional/let_array_substr9.asm new file mode 100644 index 000000000..0f5de0dba --- /dev/null +++ b/tests/functional/let_array_substr9.asm @@ -0,0 +1,1155 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + 9 + call __STORE_STR + ld hl, __LABEL1 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 65534 + push hl + ld hl, (_a + 9) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Ah + DEFB 30h + DEFB 31h + DEFB 32h + DEFB 33h + DEFB 34h + DEFB 35h + DEFB 36h + DEFB 37h + DEFB 38h + DEFB 39h +__LABEL1: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 51 "let_array_substr9.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 52 "let_array_substr9.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 53 "let_array_substr9.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/let_array_substr9.bas b/tests/functional/let_array_substr9.bas new file mode 100644 index 000000000..a49f31756 --- /dev/null +++ b/tests/functional/let_array_substr9.bas @@ -0,0 +1,4 @@ +DIM a$(10) +LET a(3) = "0123456789" +LET a(3, 1 TO) = "HELLO" + diff --git a/tests/functional/let_array_wrong_dims.bas b/tests/functional/let_array_wrong_dims.bas new file mode 100644 index 000000000..1c1a03d7a --- /dev/null +++ b/tests/functional/let_array_wrong_dims.bas @@ -0,0 +1,3 @@ +DIM a(10) as Byte +LET a(1, 2) = 0 + diff --git a/tests/functional/letarrstr_substr0.asm b/tests/functional/letarrstr_substr0.asm new file mode 100644 index 000000000..7ec334680 --- /dev/null +++ b/tests/functional/letarrstr_substr0.asm @@ -0,0 +1,828 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, (_a + 5) + call __LOADSTR + push hl + ld hl, 2 + push hl + ld hl, 4 + push hl + ld a, 1 + call __STRSLICE + ex de, hl + ld hl, _b + call __STORE_STR2 + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "alloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 30 "letarrstr_substr0.bas" +#line 1 "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 "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 + ; --------------------------------------------------------------------- + +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 + +#line 9 "storestr2.asm" + +__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 + +#line 31 "letarrstr_substr0.bas" +#line 1 "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 "strlen.asm" + + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL + +__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 + + +#line 18 "strslice.asm" + + + +__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 + +#line 32 "letarrstr_substr0.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 00, 00 +_a: + DEFW 0000h + DEFB 02h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/letarrstr_substr0.bas b/tests/functional/letarrstr_substr0.bas new file mode 100644 index 000000000..df863464b --- /dev/null +++ b/tests/functional/letarrstr_substr0.bas @@ -0,0 +1,4 @@ +DIM a$(5) + +b$ = a$(1)(2 TO 4) 'OK + diff --git a/tests/functional/letarrstr_substr1.asm b/tests/functional/letarrstr_substr1.asm new file mode 100644 index 000000000..4ef0ce8b3 --- /dev/null +++ b/tests/functional/letarrstr_substr1.asm @@ -0,0 +1,828 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, (_a + 5) + call __LOADSTR + push hl + ld hl, 2 + push hl + ld hl, 2 + push hl + ld a, 1 + call __STRSLICE + ex de, hl + ld hl, _b + call __STORE_STR2 + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "alloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 30 "letarrstr_substr1.bas" +#line 1 "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 "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 + ; --------------------------------------------------------------------- + +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 + +#line 9 "storestr2.asm" + +__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 + +#line 31 "letarrstr_substr1.bas" +#line 1 "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 "strlen.asm" + + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL + +__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 + + +#line 18 "strslice.asm" + + + +__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 + +#line 32 "letarrstr_substr1.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 00, 00 +_a: + DEFW 0000h + DEFB 02h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/letarrstr_substr1.bas b/tests/functional/letarrstr_substr1.bas new file mode 100644 index 000000000..ceb3170af --- /dev/null +++ b/tests/functional/letarrstr_substr1.bas @@ -0,0 +1,4 @@ +DIM a$(5) + +b$ = a$(1)(2) 'OK + diff --git a/tests/functional/leu32.asm b/tests/functional/leu32.asm index a4d294075..9eba6f735 100644 --- a/tests/functional/leu32.asm +++ b/tests/functional/leu32.asm @@ -132,24 +132,25 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 123 "leu32.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/line_macro.asm b/tests/functional/line_macro.asm new file mode 100644 index 000000000..d13b1cc4e --- /dev/null +++ b/tests/functional/line_macro.asm @@ -0,0 +1,39 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 5 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/line_macro.bas b/tests/functional/line_macro.bas new file mode 100644 index 000000000..a32901cb7 --- /dev/null +++ b/tests/functional/line_macro.bas @@ -0,0 +1,6 @@ + + +DIM a as Uinteger + +LET a = __LINE__ + diff --git a/tests/functional/llb.bas b/tests/functional/llb.bas new file mode 100644 index 000000000..5f0d7fbed --- /dev/null +++ b/tests/functional/llb.bas @@ -0,0 +1,4 @@ +DIM strTemp as String + +If f$(strTemp)="," then +End If diff --git a/tests/functional/llc.asm b/tests/functional/llc.asm new file mode 100644 index 000000000..9dd8bbde6 --- /dev/null +++ b/tests/functional/llc.asm @@ -0,0 +1,808 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, __LABEL0 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 1 + push hl + ld hl, (_r) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 0001h + DEFB 2Eh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 32 "llc.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 33 "llc.bas" + +ZXBASIC_USER_DATA: +_r: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/llc.bas b/tests/functional/llc.bas new file mode 100644 index 000000000..82eaae45c --- /dev/null +++ b/tests/functional/llc.bas @@ -0,0 +1 @@ +LET r$(1)="." diff --git a/tests/functional/load02.asm b/tests/functional/load02.asm index 415de1175..776974937 100644 --- a/tests/functional/load02.asm +++ b/tests/functional/load02.asm @@ -48,10 +48,12 @@ __LABEL0: DEFB 74h DEFB 31h #line 1 "load.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -59,25 +61,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -86,50 +88,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -137,12 +140,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -150,7 +154,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -158,9 +162,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -168,25 +173,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -195,39 +200,39 @@ __STOP: ; | (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 + ; 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 @@ -235,57 +240,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -297,39 +302,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -337,15 +342,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -370,14 +375,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -385,24 +390,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "load.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -410,25 +416,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -437,38 +443,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -477,57 +483,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -536,47 +542,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -586,20 +592,22 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "load.asm" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -607,45 +615,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -658,65 +667,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -725,15 +736,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -741,26 +754,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -774,44 +787,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -822,27 +836,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -850,27 +865,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -879,48 +895,50 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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) - -#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -929,32 +947,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -964,7 +982,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -972,19 +990,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -994,7 +1013,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1005,18 +1024,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1026,7 +1046,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1037,18 +1057,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1058,7 +1079,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1071,21 +1092,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1094,79 +1116,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1175,75 +1197,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1253,7 +1275,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1265,16 +1287,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1288,49 +1310,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1341,17 +1363,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1364,27 +1386,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1392,10 +1414,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1412,80 +1434,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1502,8 +1524,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1527,17 +1549,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1548,7 +1570,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1558,21 +1580,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1591,9 +1613,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1618,12 +1640,12 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 4 "load.asm" - + LOAD_CODE: ; This function will implement the LOAD CODE Routine ; Parameters in the stack are HL => String with LOAD name @@ -1631,9 +1653,9 @@ LOAD_CODE: ; 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 @@ -1644,50 +1666,50 @@ LOAD_CODE: LOCAL LD_CH_PR LOCAL LOAD_END LOCAL VR_CONTROL, VR_CONT_1, VR_CONT_2 - + 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 - + LD_BYTES EQU 0556h ; ROM Routine LD-BYTES 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 - + 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 @@ -1698,7 +1720,7 @@ __LOAD_CODE: ; INLINE version ldir pop bc pop hl - + LOAD_HEADER: ex de, hl ; Saves HL in DE ld hl, 10 @@ -1707,46 +1729,46 @@ LOAD_HEADER: 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: ld bc, 17; 2nd Header call __MEM_ALLOC - + ld a, h or l jr nz, LOAD_CONT3; there's memory - + ld a, ERROR_OutOfMemory jp __ERROR - + 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: @@ -1758,22 +1780,22 @@ LD_TYPE: 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 + + 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 + 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 @@ -1781,32 +1803,32 @@ LD_NAME: 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: call __PRINTCHAR ; PRINT-A prints character 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. - + ld a, 0Dh ; prepare carriage return. call __PRINTCHAR ; PRINT-A outputs it. - + 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 @@ -1817,7 +1839,7 @@ VR_CONTROL: ; e.g. LOAD "x" CODE sbc hl, de jr nz, LOAD_ERROR ; Lenghts don't match - + VR_CONT_1: ld hl, HEAD1 + 13 ; fetch start of old data (orig. header) ld a, (hl) @@ -1826,56 +1848,56 @@ VR_CONT_1: ld l, a or h ; check start for zero (unespecified) 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) + 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: pop ix ; Recovers stack frame pointer ld hl, (TMP_HEADER) ; Recovers tmp_header pointer jp MEM_FREE ; Returns via FREE_MEM, freeing tmp header - + ENDP - - + + 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 + ; 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 - + 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: @@ -1884,31 +1906,32 @@ LOOK_NEXT_TAPE_MSG: 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 #line 35 "load02.bas" #line 1 "loadstr.asm" - - + + + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -1917,37 +1940,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 36 "load02.bas" - + ZXBASIC_USER_DATA: _variableToSave: DEFB 00, 00 diff --git a/tests/functional/load03.asm b/tests/functional/load03.asm index ecd56d9bb..3b1d5afc9 100644 --- a/tests/functional/load03.asm +++ b/tests/functional/load03.asm @@ -47,10 +47,12 @@ __LABEL0: DEFB 73h DEFB 74h #line 1 "load.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -58,25 +60,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -85,50 +87,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -136,12 +139,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -149,7 +153,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -157,9 +161,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -167,25 +172,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -194,39 +199,39 @@ __STOP: ; | (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 + ; 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 @@ -234,57 +239,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -296,39 +301,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -336,15 +341,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -369,14 +374,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -384,24 +389,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "load.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -409,25 +415,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -436,38 +442,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -476,57 +482,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -535,47 +541,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -585,20 +591,22 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "load.asm" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -606,45 +614,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -657,65 +666,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -724,15 +735,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -740,26 +753,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -773,44 +786,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -821,27 +835,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -849,27 +864,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -878,48 +894,50 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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) - -#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + +#line 63 "/src/zxb/trunk/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -928,32 +946,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -963,7 +981,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -971,19 +989,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -993,7 +1012,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1004,18 +1023,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1025,7 +1045,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1036,18 +1056,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1057,7 +1078,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1070,21 +1091,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1093,79 +1115,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1174,75 +1196,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1252,7 +1274,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1264,16 +1286,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1287,49 +1309,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1340,17 +1362,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1363,27 +1385,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1391,10 +1413,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1411,80 +1433,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1501,8 +1523,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1526,17 +1548,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1547,7 +1569,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1557,21 +1579,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1590,9 +1612,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1617,12 +1639,12 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 4 "load.asm" - + LOAD_CODE: ; This function will implement the LOAD CODE Routine ; Parameters in the stack are HL => String with LOAD name @@ -1630,9 +1652,9 @@ LOAD_CODE: ; 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 @@ -1643,50 +1665,50 @@ LOAD_CODE: LOCAL LD_CH_PR LOCAL LOAD_END LOCAL VR_CONTROL, VR_CONT_1, VR_CONT_2 - + 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 - + LD_BYTES EQU 0556h ; ROM Routine LD-BYTES 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 - + 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 @@ -1697,7 +1719,7 @@ __LOAD_CODE: ; INLINE version ldir pop bc pop hl - + LOAD_HEADER: ex de, hl ; Saves HL in DE ld hl, 10 @@ -1706,46 +1728,46 @@ LOAD_HEADER: 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: ld bc, 17; 2nd Header call __MEM_ALLOC - + ld a, h or l jr nz, LOAD_CONT3; there's memory - + ld a, ERROR_OutOfMemory jp __ERROR - + 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: @@ -1757,22 +1779,22 @@ LD_TYPE: 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 + + 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 + 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 @@ -1780,32 +1802,32 @@ LD_NAME: 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: call __PRINTCHAR ; PRINT-A prints character 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. - + ld a, 0Dh ; prepare carriage return. call __PRINTCHAR ; PRINT-A outputs it. - + 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 @@ -1816,7 +1838,7 @@ VR_CONTROL: ; e.g. LOAD "x" CODE sbc hl, de jr nz, LOAD_ERROR ; Lenghts don't match - + VR_CONT_1: ld hl, HEAD1 + 13 ; fetch start of old data (orig. header) ld a, (hl) @@ -1825,56 +1847,56 @@ VR_CONT_1: ld l, a or h ; check start for zero (unespecified) 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) + 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: pop ix ; Recovers stack frame pointer ld hl, (TMP_HEADER) ; Recovers tmp_header pointer jp MEM_FREE ; Returns via FREE_MEM, freeing tmp header - + ENDP - - + + 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 + ; 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 - + 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: @@ -1883,31 +1905,32 @@ LOOK_NEXT_TAPE_MSG: 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 #line 34 "load03.bas" #line 1 "loadstr.asm" - - + + + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -1916,37 +1939,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 35 "load03.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/loadstr.asm b/tests/functional/loadstr.asm index 23e066ea1..2613f8616 100644 --- a/tests/functional/loadstr.asm +++ b/tests/functional/loadstr.asm @@ -44,6 +44,7 @@ __LABEL0: DEFB 31h DEFB 30h #line 1 "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$) @@ -54,13 +55,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -68,25 +71,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -95,51 +98,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -147,12 +151,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -160,7 +165,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -168,9 +173,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -178,25 +184,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -205,41 +211,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -247,25 +254,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -274,39 +281,39 @@ __STOP: ; | (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 + ; 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 @@ -314,57 +321,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -376,39 +383,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -416,15 +423,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -449,14 +456,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -464,24 +471,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -489,25 +497,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -516,38 +524,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -556,57 +564,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -615,47 +623,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -665,12 +673,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -689,29 +697,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -720,111 +728,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -840,7 +848,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -848,124 +856,127 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 32 "loadstr.bas" #line 1 "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. - - - + + + __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 - + #line 33 "loadstr.bas" #line 1 "str.asm" + ; 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 - - + + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -982,8 +993,9 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK jp __FPSTACK_PUSH #line 9 "str.asm" #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -991,89 +1003,90 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 10 "str.asm" - + __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 + 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 - + #line 34 "loadstr.bas" #line 1 "val.asm" - - - - + + + + + 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 @@ -1083,107 +1096,107 @@ VAL: ; Computes VAL(a$) using ROM FP-CALC 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 - + 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 hl, 0 add hl, sp ld (ERR_SP), hl - + 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_Ok ld (ERR_NR), a - + xor a ld b, a ld c, a ld d, b ld e, c ret - + ENDP - + #line 35 "loadstr.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/loadu16ii.asm b/tests/functional/loadu16ii.asm index 6e3eb2d35..9f64911bb 100644 --- a/tests/functional/loadu16ii.asm +++ b/tests/functional/loadu16ii.asm @@ -53,18 +53,19 @@ __LABEL__test: ld c, l jp __END_PROGRAM #line 1 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -82,41 +83,43 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 43 "loadu16ii.bas" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -124,45 +127,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -175,41 +179,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -217,12 +223,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -230,50 +237,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -282,15 +290,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -298,26 +308,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -331,44 +341,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -379,27 +390,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -407,27 +419,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -436,81 +449,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -519,32 +534,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -554,7 +569,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -562,19 +577,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -584,7 +600,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -595,18 +611,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -616,7 +633,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -627,18 +644,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -648,7 +666,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -661,21 +679,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -684,79 +703,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -765,75 +784,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -843,7 +862,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -855,16 +874,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -878,49 +897,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -931,17 +950,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -954,27 +973,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -982,10 +1001,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1002,80 +1021,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1092,8 +1111,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1117,17 +1136,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1138,7 +1157,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1148,21 +1167,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1181,9 +1200,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1208,30 +1227,33 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 44 "loadu16ii.bas" #line 1 "printu16.asm" + #line 1 "printi16.asm" + #line 1 "printnum.asm" - - - + + + + __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 @@ -1239,28 +1261,30 @@ __PRINTU_CONT: pop bc djnz __PRINTU_CONT ret - + ENDP - - + + __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER mus preserve registers ld a, '-' jp __PRINT_DIGIT - + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - + + #line 2 "printi16.asm" #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -1270,23 +1294,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -1295,46 +1319,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -1342,75 +1366,75 @@ __DIVI16_FAST: 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 + + 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 - + #line 3 "printi16.asm" - - - + + + __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 - + #line 2 "printu16.asm" - + #line 45 "loadu16ii.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/localdim.asm b/tests/functional/localdim.asm index 8092550ee..79ef97082 100644 --- a/tests/functional/localdim.asm +++ b/tests/functional/localdim.asm @@ -57,7 +57,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL0: DEFB 01h diff --git a/tests/functional/ltee1.asm b/tests/functional/ltee1.asm index cbd832bdc..f296066f8 100644 --- a/tests/functional/ltee1.asm +++ b/tests/functional/ltee1.asm @@ -101,9 +101,10 @@ __LABEL1: DEFB 6Ch DEFB 65h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -111,25 +112,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -138,40 +139,41 @@ __LABEL1: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -179,25 +181,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -206,39 +208,39 @@ __LABEL1: ; | (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 + ; 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 @@ -246,56 +248,56 @@ __LABEL1: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -304,57 +306,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -363,47 +365,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -413,15 +415,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 88 "ltee1.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -429,25 +433,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -456,50 +460,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -507,12 +512,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -520,16 +526,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -541,39 +547,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -581,15 +587,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -614,14 +620,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -629,25 +635,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -656,30 +662,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -687,19 +693,22 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 89 "ltee1.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -707,45 +716,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -758,65 +768,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -825,15 +837,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -841,26 +855,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -874,44 +888,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -922,27 +937,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -950,27 +966,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -979,81 +996,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1062,32 +1081,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1097,7 +1116,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1105,19 +1124,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1127,7 +1147,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1138,18 +1158,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1159,7 +1180,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1170,18 +1191,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1191,7 +1213,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1204,21 +1226,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1227,79 +1250,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1308,75 +1331,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1386,7 +1409,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1398,16 +1421,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1421,49 +1444,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1474,17 +1497,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1497,27 +1520,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1525,10 +1548,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1545,80 +1568,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1635,8 +1658,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1660,17 +1683,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1681,7 +1704,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1691,21 +1714,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1724,9 +1747,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1751,63 +1774,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 90 "ltee1.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1816,18 +1840,20 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 91 "ltee1.bas" #line 1 "pstorestr.asm" + ; vim:ts=4:et:sw=4 - ; + ; ; 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 ; - + #line 1 "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$) @@ -1838,13 +1864,15 @@ __PRINT_STR: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1852,25 +1880,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1879,42 +1907,42 @@ __PRINT_STR: ; | (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 + ; 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. @@ -1933,29 +1961,29 @@ __PRINT_STR: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -1964,111 +1992,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -2084,7 +2112,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -2092,204 +2120,208 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 8 "pstorestr.asm" - + __PSTORE_STR: push ix pop hl add hl, bc jp __STORE_STR - + #line 92 "ltee1.bas" #line 1 "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 "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. - - - + + + __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 - + #line 9 "pstorestr2.asm" - + __PSTORE_STR2: push ix pop hl add hl, bc jp __STORE_STR2 - + #line 93 "ltee1.bas" - + #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -2297,50 +2329,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 95 "ltee1.bas" - + ZXBASIC_USER_DATA: _newMsg: DEFB 00, 00 diff --git a/tests/functional/ltee10.asm b/tests/functional/ltee10.asm new file mode 100644 index 000000000..e9315333a --- /dev/null +++ b/tests/functional/ltee10.asm @@ -0,0 +1,274 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_setlocal: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + push hl + push ix + pop hl + ld bc, -6 + add hl, bc + ex de, hl + ld hl, __LABEL0 + ld bc, 6 + ldir + ld a, (_pos) + ld l, a + ld h, 0 + push hl + push ix + pop hl + ld de, -6 + add hl, de + call __ARRAY + ld (hl), 3 +_setlocal__leave: + ld sp, ix + pop ix + ret +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/src/zxb/trunk/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 48 "ltee10.bas" + +ZXBASIC_USER_DATA: +_pos: + DEFB 00 +__LABEL0: + DEFB 00h + DEFB 00h + DEFB 01h + DEFB 00h + DEFB 01h + DEFB 02h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ltee10.bas b/tests/functional/ltee10.bas new file mode 100644 index 000000000..fb3949b01 --- /dev/null +++ b/tests/functional/ltee10.bas @@ -0,0 +1,7 @@ +dim pos as UBYTE + +sub setlocal + dim testglobal(2) as Byte => {0, 1, 2} + testglobal(pos) = 3 +end sub + diff --git a/tests/functional/ltee3.asm b/tests/functional/ltee3.asm index 97dcb45c7..d5d54226f 100644 --- a/tests/functional/ltee3.asm +++ b/tests/functional/ltee3.asm @@ -63,9 +63,10 @@ __LABEL0: DEFB 6Eh DEFB 67h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -73,25 +74,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -100,40 +101,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -141,25 +143,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -168,39 +170,39 @@ __LABEL0: ; | (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 + ; 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 @@ -208,56 +210,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -266,57 +268,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -325,47 +327,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -375,15 +377,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 51 "ltee3.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -391,25 +395,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -418,50 +422,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -469,12 +474,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -482,16 +488,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -503,39 +509,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -543,15 +549,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -576,14 +582,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -591,25 +597,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -618,37 +624,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 52 "ltee3.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/ltee4.asm b/tests/functional/ltee4.asm index e2a492db1..27f705c1c 100644 --- a/tests/functional/ltee4.asm +++ b/tests/functional/ltee4.asm @@ -40,7 +40,7 @@ _hsGetName__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: _hsName: DEFW 0000h diff --git a/tests/functional/ltee5.asm b/tests/functional/ltee5.asm index 439d77e5a..3444c55ff 100644 --- a/tests/functional/ltee5.asm +++ b/tests/functional/ltee5.asm @@ -74,9 +74,10 @@ __LABEL1: DEFB 61h DEFB 6Ch #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -84,25 +85,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -111,40 +112,41 @@ __LABEL1: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -152,25 +154,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -179,39 +181,39 @@ __LABEL1: ; | (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 + ; 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 @@ -219,56 +221,56 @@ __LABEL1: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -277,57 +279,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -336,47 +338,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -386,18 +388,20 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 62 "ltee5.bas" #line 1 "pstorestr.asm" + ; vim:ts=4:et:sw=4 - ; + ; ; 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 ; - + #line 1 "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$) @@ -408,13 +412,15 @@ __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 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -422,25 +428,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -449,51 +455,52 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -501,12 +508,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -514,7 +522,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -522,9 +530,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -532,25 +541,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -559,40 +568,40 @@ __STOP: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -604,39 +613,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -644,15 +653,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -677,14 +686,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -692,23 +701,23 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -727,29 +736,29 @@ __MEM_SUBTRACT: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -758,111 +767,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -878,7 +887,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -886,53 +895,53 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 8 "pstorestr.asm" - + __PSTORE_STR: push ix pop hl add hl, bc jp __STORE_STR - + #line 63 "ltee5.bas" - - + + ZXBASIC_USER_DATA: _testglobal: DEFB 00, 00 diff --git a/tests/functional/ltee6.asm b/tests/functional/ltee6.asm index 47a96d7f7..74c44c58d 100644 --- a/tests/functional/ltee6.asm +++ b/tests/functional/ltee6.asm @@ -46,9 +46,10 @@ __LABEL0: DEFB 61h DEFB 6Ch #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -56,27 +57,28 @@ __LABEL0: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -94,44 +96,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -139,23 +141,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -164,13 +165,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -178,75 +177,76 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 34 "ltee6.bas" #line 1 "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$) @@ -257,13 +257,15 @@ __FNMUL2: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -271,25 +273,25 @@ __FNMUL2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -298,51 +300,52 @@ __FNMUL2: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -350,12 +353,13 @@ __FNMUL2: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -363,7 +367,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -371,9 +375,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -381,25 +386,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -408,41 +413,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -450,25 +456,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -477,39 +483,39 @@ __STOP: ; | (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 + ; 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 @@ -517,57 +523,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -579,39 +585,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -619,15 +625,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -652,14 +658,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -667,24 +673,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -692,25 +699,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -719,38 +726,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -759,57 +766,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -818,47 +825,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -868,12 +875,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -892,29 +899,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -923,111 +930,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -1043,7 +1050,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -1051,44 +1058,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 35 "ltee6.bas" - + ZXBASIC_USER_DATA: _pos: DEFB 00 diff --git a/tests/functional/ltee7.asm b/tests/functional/ltee7.asm index a2dcfaacd..a48906068 100644 --- a/tests/functional/ltee7.asm +++ b/tests/functional/ltee7.asm @@ -84,9 +84,10 @@ __LABEL0: DEFB 6Ch DEFB 6Fh #line 1 "array.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ------------------------------------------------------------------- ; Simple array Index routine @@ -94,27 +95,28 @@ __LABEL0: ; 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 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -132,44 +134,44 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 20 "array.asm" - -#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + __ARRAY: PROC - + LOCAL LOOP LOCAL ARRAY_END LOCAL RET_ADDRESS ; Stores return address - + ex (sp), hl ; Return address in HL, array address in the stack ld (RET_ADDRESS + 1), hl ; Stores it for later - + exx pop hl ; Will use H'L' as the pointer ld c, (hl) ; Loads Number of dimensions from (hl) @@ -177,23 +179,22 @@ __ARRAY: ld b, (hl) inc hl ; Ready exx - + ld hl, 0 ; BC = Offset "accumulator" - -#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + add hl, bc ; 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 - + ld e, (hl) ; Loads next dimension into D'E' inc hl ld d, (hl) @@ -202,13 +203,11 @@ LOOP: dec bc ; Decrements loop counter exx pop de ; DE = Max bound Number (i-th dimension) - -#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP - + ARRAY_END: ld e, (hl) inc hl @@ -216,83 +215,85 @@ ARRAY_END: push hl push de exx - -#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +#line 92 "/src/zxb/trunk/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP - + ex de, hl ld hl, 0 pop bc ld b, c -ARRAY_SIZE_LOOP: +ARRAY_SIZE_LOOP: add hl, de djnz ARRAY_SIZE_LOOP - + ;; Even faster ;pop bc - + ;ld d, h ;ld e, l - + ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;jp z, __ARRAY_FIN - + ;add hl, hl ;dec c ;dec c ;jp z, __ARRAY_FIN - + ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" - + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + pop de add hl, de ; Adds element start - + RET_ADDRESS: ld de, 0 push de ret ; HL = (Start of Elements + Offset) - + ;; Performs a faster multiply for little 16bit numbs LOCAL __FNMUL, __FNMUL2 - + __FNMUL: xor a or d jp nz, __MUL16_FAST - + or e ex de, hl ret z - + cp 33 jp nc, __MUL16_FAST - + ld b, l ld l, h ; HL = 0 - + __FNMUL2: add hl, de djnz __FNMUL2 ret - + ENDP - + #line 72 "ltee7.bas" #line 1 "arrayfree.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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -300,25 +301,25 @@ __FNMUL2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -327,40 +328,41 @@ __FNMUL2: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -368,25 +370,25 @@ __FNMUL2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -395,39 +397,39 @@ __FNMUL2: ; | (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 + ; 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 @@ -435,56 +437,56 @@ __FNMUL2: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -493,57 +495,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -552,47 +554,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -602,30 +604,30 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 6 "arrayfree.asm" - + __ARRAY_FREE: PROC - + LOCAL __ARRAY_LOOP - + ex de, hl pop hl ; (ret address) ex (sp), hl ; Callee -> HL = Number of elements - - ex de, hl - + + ex de, hl + __ARRAY_FREE_FAST: ; Fastcall entry: DE = Number of elements ld a, h or l ret z ; ret if NULL - + ld b, d ld c, e - + ld e, (hl) inc hl ld d, (hl) @@ -634,44 +636,45 @@ __ARRAY_FREE_FAST: ; Fastcall entry: DE = Number of elements add hl, hl ; HL = HL * 2 add hl, de inc hl ; HL now points to the element start - + __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 - - + + __ARRAY_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 - + ex de, hl + push hl ; Saves array pointer for later call __ARRAY_FREE_FAST pop hl ; recovers array block pointer - + jp __MEM_FREE ; Frees it and returns from __MEM_FREE - + #line 73 "ltee7.bas" #line 1 "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$) @@ -682,13 +685,15 @@ __ARRAY_FREE_MEM: ; like the above, buf also frees the array itself ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -696,25 +701,25 @@ __ARRAY_FREE_MEM: ; like the above, buf also frees the array itself ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -723,51 +728,52 @@ __ARRAY_FREE_MEM: ; like the above, buf also frees the array itself ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -775,12 +781,13 @@ __ARRAY_FREE_MEM: ; like the above, buf also frees the array itself ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -788,7 +795,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -796,9 +803,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -806,25 +814,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -833,40 +841,40 @@ __STOP: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -878,39 +886,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -918,15 +926,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -951,14 +959,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -966,23 +974,23 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -1001,29 +1009,29 @@ __MEM_SUBTRACT: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -1032,111 +1040,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -1152,7 +1160,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -1160,44 +1168,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 74 "ltee7.bas" - + ZXBASIC_USER_DATA: _pos: DEFB 00 diff --git a/tests/functional/ltee8.asm b/tests/functional/ltee8.asm index 53682cff8..56c0ecec1 100644 --- a/tests/functional/ltee8.asm +++ b/tests/functional/ltee8.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/ltee9.asm b/tests/functional/ltee9.asm index 140138bed..42efe7f4f 100644 --- a/tests/functional/ltee9.asm +++ b/tests/functional/ltee9.asm @@ -94,15 +94,17 @@ _start__leave: pop ix ret #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -112,23 +114,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -137,46 +139,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -184,43 +186,45 @@ __DIVI16_FAST: 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 + + 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 - + #line 85 "ltee9.bas" #line 1 "mul32.asm" + #line 1 "_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 - - + + __MUL32_64START: push hl exx @@ -229,18 +233,18 @@ __MUL32_64START: 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: and a ; reset carry flag sbc hl,hl ; result bits 32..47 = 0 @@ -249,8 +253,8 @@ __LMUL: exx ld a,b ; mpr is b'c'ac ld b,33 ; initialize loop counter - jp __LMULSTART - + 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 @@ -259,7 +263,7 @@ __LMULLOOP: exx adc hl,de exx - + __LMULNOADD: exx rr h ; right shift upper @@ -267,7 +271,7 @@ __LMULNOADD: exx rr h rr l - + __LMULSTART: exx rr b ; right shift mpr/ @@ -276,11 +280,11 @@ __LMULSTART: rra ; equivalent to rr a rr c djnz __LMULLOOP - + ret ; result in h'l'hlb'c'ac - + #line 2 "mul32.asm" - + __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 @@ -291,7 +295,7 @@ __MUL32: ; multiplies 32 bit un/signed integer. 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 @@ -300,24 +304,26 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) ld h, a ld l, c ret - - + + #line 86 "ltee9.bas" #line 1 "pstore32.asm" + #line 1 "store32.asm" + __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 @@ -327,9 +333,9 @@ __STORE32: ; Stores the given integer in DEBC at address HL inc hl ld (hl), d ret - + #line 2 "pstore32.asm" - + ; Stores a 32 bit integer number (DE,HL) at (IX + BC) __PSTORE32: push hl @@ -340,35 +346,36 @@ __PSTORE32: jp __STORE32 #line 87 "ltee9.bas" #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 88 "ltee9.bas" - + ZXBASIC_USER_DATA: _x: DEFB 00, 00 diff --git a/tests/functional/ltf16.asm b/tests/functional/ltf16.asm index d7ff1b1ea..dea2de2d5 100644 --- a/tests/functional/ltf16.asm +++ b/tests/functional/ltf16.asm @@ -84,50 +84,52 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti32.asm" - + + #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 3 "lti32.asm" - + __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 @@ -139,24 +141,25 @@ checkParity: ENDP #line 75 "ltf16.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 76 "ltf16.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/lti32c.asm b/tests/functional/lti32c.asm index 60159b495..e2e640165 100644 --- a/tests/functional/lti32c.asm +++ b/tests/functional/lti32c.asm @@ -97,50 +97,52 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "lti32.asm" - + + #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 3 "lti32.asm" - + __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 @@ -152,24 +154,25 @@ checkParity: ENDP #line 88 "lti32c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 89 "lti32c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/ltu32c.asm b/tests/functional/ltu32c.asm index d723cc686..64355af68 100644 --- a/tests/functional/ltu32c.asm +++ b/tests/functional/ltu32c.asm @@ -102,53 +102,55 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 93 "ltu32c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 94 "ltu32c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/lvalsubstr_nolet.asm b/tests/functional/lvalsubstr_nolet.asm new file mode 100644 index 000000000..c0fe1dacd --- /dev/null +++ b/tests/functional/lvalsubstr_nolet.asm @@ -0,0 +1,808 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, __LABEL0 + call __LOADSTR + push hl + xor a + push af + ld hl, 1 + push hl + ld hl, 1 + push hl + ld hl, (_a) + call __LETSUBSTR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 0001h + DEFB 2Eh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 11 "letsubstr.asm" + +__LETSUBSTR: + PROC + + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + LOCAL __FREE_STR0 + + 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 + + ld a, h + or l + jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather + exx ; Return if p0 > p1 + + or a + sbc hl, bc ; P0 >= String length? + exx + + jp z, __FREE_STR0 ; Return if equal + jp c, __FREE_STR0 ; Return if greather + + 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 subtring 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) + + exx + ex de, hl + +__FREE_STR0: + ex de, hl + +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + + ENDP + +#line 32 "lvalsubstr_nolet.bas" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 33 "lvalsubstr_nolet.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/lvalsubstr_nolet.bas b/tests/functional/lvalsubstr_nolet.bas new file mode 100644 index 000000000..e57d2cdb5 --- /dev/null +++ b/tests/functional/lvalsubstr_nolet.bas @@ -0,0 +1,3 @@ +DIM a$ +a$(1)="." + diff --git a/tests/functional/mcleod.asm b/tests/functional/mcleod.asm index 3c12ac614..8344c5eda 100644 --- a/tests/functional/mcleod.asm +++ b/tests/functional/mcleod.asm @@ -38,50 +38,53 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -89,96 +92,114 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 29 "mcleod.bas" #line 1 "mulf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -194,7 +215,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "mulf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -203,131 +224,40 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __MULF: ; Multiplication call __FPSTACK_PUSH2 - + ; ------------- ROM MUL rst 28h - defb 04h ; + defb 04h ; defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 30 "mcleod.bas" #line 1 "random.asm" + ; RANDOM functions - -#line 1 "mul32.asm" -#line 1 "_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 - - -__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: - and a ; reset carry flag - sbc hl,hl ; result bits 32..47 = 0 - exx - sbc hl,hl ; result bits 48..63 = 0 - 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 - -#line 2 "mul32.asm" - -__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 - - -#line 4 "random.asm" - + 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) @@ -335,18 +265,18 @@ TAKE_FRAMES: ld hl, (FRAMES + 2) ld (RANDOM_SEED_HIGH), hl ret - + FRAMES EQU 23672 ENDP - + RANDOM_SEED_HIGH EQU RAND+6 ; RANDOM seed, 16 higher bits RANDOM_SEED_LOW EQU 23670 ; RANDOM seed, 16 lower bits - - + + RAND: PROC LOCAL RAND_LOOP - ld b, 4 + ld b, 4 RAND_LOOP: ld hl,(RANDOM_SEED_LOW) ; xz -> yw ld de,0C0DEh ; yw -> zt @@ -378,30 +308,30 @@ RAND_LOOP: ld h, a 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: @@ -411,22 +341,22 @@ RND_LOOP: 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 d rr c rr b - + ld e, a ; E must have the highest byte ld a, l ; exponent in A ret - + ENDP - + #line 31 "mcleod.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/mcleod2.asm b/tests/functional/mcleod2.asm index c88bfdc77..dd0f78606 100644 --- a/tests/functional/mcleod2.asm +++ b/tests/functional/mcleod2.asm @@ -35,6 +35,7 @@ __CALL_BACK__: __LABEL0: DEFW 0000h #line 1 "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$) @@ -45,13 +46,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -59,25 +62,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -86,51 +89,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -138,12 +142,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -151,7 +156,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -159,9 +164,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -169,25 +175,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -196,41 +202,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -238,25 +245,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -265,39 +272,39 @@ __STOP: ; | (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 + ; 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 @@ -305,57 +312,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -367,39 +374,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -407,15 +414,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -440,14 +447,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -455,24 +462,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -480,25 +488,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -507,38 +515,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -547,57 +555,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -606,47 +614,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -656,12 +664,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -680,29 +688,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -711,111 +719,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -831,7 +839,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -839,44 +847,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 23 "mcleod2.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/memcpytest.asm b/tests/functional/memcpytest.asm new file mode 100644 index 000000000..81855f7cb --- /dev/null +++ b/tests/functional/memcpytest.asm @@ -0,0 +1,1689 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + call CLS + ld a, 1 + ld (_i), a + jp __LABEL0 +__LABEL3: + ld hl, __LABEL5 + xor a + call __PRINTSTR +__LABEL4: + ld hl, _i + inc (hl) +__LABEL0: + ld a, 10 + ld hl, (_i - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + ld hl, 6912 + push hl + ld hl, 40000 + push hl + ld hl, 16384 + call _MemCopy + call CLS + ld hl, 0 + call __PAUSE + ld hl, 6912 + push hl + ld hl, 16384 + push hl + ld hl, 40000 + call _MemCopy + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_MemMove: +#line 30 + exx + pop hl + exx + pop de + pop bc + exx + push hl + exx + jp __MEMCPY +#line 39 +_MemMove__leave: + ret +_MemCopy: +#line 64 + exx + pop hl + exx + pop de + pop bc + exx + push hl + exx + ldir +#line 73 +_MemCopy__leave: + ret +_MemSet: +#line 95 + 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 +#line 108 +_MemSet__leave: + ret +__LABEL5: + DEFW 0005h + DEFB 54h + DEFB 45h + DEFB 53h + DEFB 54h + DEFB 20h +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + +#line 1 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 9 "cls.asm" + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 103 "memcpytest.bas" +#line 1 "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 + +__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 +#line 104 "memcpytest.bas" +#line 1 "pause.asm" + + ; The PAUSE statement (Calling the ROM) + +__PAUSE: + ld b, h + ld c, l + jp 1F3Dh ; PAUSE_1 +#line 105 "memcpytest.bas" +#line 1 "printstr.asm" + +#line 1 "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 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 2 "printstr.asm" + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 106 "memcpytest.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/memcpytest.bas b/tests/functional/memcpytest.bas new file mode 100644 index 000000000..e5a086286 --- /dev/null +++ b/tests/functional/memcpytest.bas @@ -0,0 +1,13 @@ + +CLS +FOR i = 1 to 10 + PRINT "TEST "; +NEXT + +#include + +memcopy(16384, 40000, 6912) +cls +pause 0 +memcopy(40000, 16384, 6912) + diff --git a/tests/functional/modf16.asm b/tests/functional/modf16.asm index 5c2e29772..263451ce7 100644 --- a/tests/functional/modf16.asm +++ b/tests/functional/modf16.asm @@ -33,7 +33,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/modf16c.asm b/tests/functional/modf16c.asm index 4c3194f7a..6879491b6 100644 --- a/tests/functional/modf16c.asm +++ b/tests/functional/modf16c.asm @@ -77,43 +77,47 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "modf16.asm" + ; Computes A % B for fixed values - + #line 1 "divf16.asm" + #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -125,7 +129,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -141,9 +145,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -151,29 +155,29 @@ __DIV32LOOP: 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 @@ -183,34 +187,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -218,135 +222,137 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 2 "divf16.asm" - - -__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 - + + +__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 + #line 4 "modf16.asm" #line 1 "mulf16.asm" - + + #line 1 "_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 - - + + __MUL32_64START: push hl exx @@ -355,18 +361,18 @@ __MUL32_64START: 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: and a ; reset carry flag sbc hl,hl ; result bits 32..47 = 0 @@ -375,8 +381,8 @@ __LMUL: exx ld a,b ; mpr is b'c'ac ld b,33 ; initialize loop counter - jp __LMULSTART - + 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 @@ -385,7 +391,7 @@ __LMULLOOP: exx adc hl,de exx - + __LMULNOADD: exx rr h ; right shift upper @@ -393,7 +399,7 @@ __LMULNOADD: exx rr h rr l - + __LMULSTART: exx rr b ; right shift mpr/ @@ -402,29 +408,29 @@ __LMULSTART: rra ; equivalent to rr a rr c djnz __LMULLOOP - + ret ; result in h'l'hlb'c'ac - + #line 3 "mulf16.asm" - -__MULF16: ; + +__MULF16: ; ld a, d ; load sgn into a ex af, af' ; saves it call __ABS32 ; convert to positive - - exx + + 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 @@ -434,73 +440,74 @@ __ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 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 + 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 - + + ret + #line 5 "modf16.asm" - + __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 + 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 - + #line 68 "modf16c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 69 "modf16c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/modi32c.asm b/tests/functional/modi32c.asm index 85145cb7e..590a049eb 100644 --- a/tests/functional/modi32c.asm +++ b/tests/functional/modi32c.asm @@ -80,39 +80,41 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -124,7 +126,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -140,9 +142,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -150,29 +152,29 @@ __DIV32LOOP: 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 @@ -182,34 +184,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -217,61 +219,62 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 71 "modi32c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 72 "modi32c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/modi8.asm b/tests/functional/modi8.asm index 03b2b7b26..2559c3d42 100644 --- a/tests/functional/modi8.asm +++ b/tests/functional/modi8.asm @@ -53,49 +53,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -103,45 +104,45 @@ __DIV8A: 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 - + 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 - + #line 44 "modi8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/modi8a.asm b/tests/functional/modi8a.asm index 1847ccade..35da3f652 100644 --- a/tests/functional/modi8a.asm +++ b/tests/functional/modi8a.asm @@ -33,49 +33,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -83,45 +84,45 @@ __DIV8A: 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 - + 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 - + #line 24 "modi8a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/modi8b.asm b/tests/functional/modi8b.asm index 0b08920db..35c4edf0c 100644 --- a/tests/functional/modi8b.asm +++ b/tests/functional/modi8b.asm @@ -39,49 +39,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -89,45 +90,45 @@ __DIV8A: 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 - + 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 - + #line 30 "modi8b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/modu32c.asm b/tests/functional/modu32c.asm index 1e6dd02a6..0ddc43c2b 100644 --- a/tests/functional/modu32c.asm +++ b/tests/functional/modu32c.asm @@ -80,39 +80,41 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div32.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "div32.asm" - + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -124,7 +126,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -140,9 +142,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -150,29 +152,29 @@ __DIV32LOOP: 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 @@ -182,34 +184,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -217,61 +219,62 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 71 "modu32c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 72 "modu32c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/modu8.asm b/tests/functional/modu8.asm index 6ec5f3e6f..703bee1fe 100644 --- a/tests/functional/modu8.asm +++ b/tests/functional/modu8.asm @@ -53,49 +53,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -103,45 +104,45 @@ __DIV8A: 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 - + 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 - + #line 44 "modu8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/modu8a.asm b/tests/functional/modu8a.asm index fc0766aec..cf1ced74f 100644 --- a/tests/functional/modu8a.asm +++ b/tests/functional/modu8a.asm @@ -33,49 +33,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -83,45 +84,45 @@ __DIV8A: 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 - + 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 - + #line 24 "modu8a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/modu8b.asm b/tests/functional/modu8b.asm index 73aca2a7f..ea362d097 100644 --- a/tests/functional/modu8b.asm +++ b/tests/functional/modu8b.asm @@ -39,49 +39,50 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -89,45 +90,45 @@ __DIV8A: 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 - + 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 - + #line 30 "modu8b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/mul16.asm b/tests/functional/mul16.asm index 44e77d986..e96f2153e 100644 --- a/tests/functional/mul16.asm +++ b/tests/functional/mul16.asm @@ -39,18 +39,19 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -68,32 +69,32 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 30 "mul16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/mul16a.asm b/tests/functional/mul16a.asm index 41c625f7f..fb02a40c4 100644 --- a/tests/functional/mul16a.asm +++ b/tests/functional/mul16a.asm @@ -34,18 +34,19 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -63,32 +64,32 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 25 "mul16a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/mul16b.asm b/tests/functional/mul16b.asm index ee4e0c06d..e47df00d6 100644 --- a/tests/functional/mul16b.asm +++ b/tests/functional/mul16b.asm @@ -40,18 +40,19 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mul16.asm" + __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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand ;; ld c, h ;; ld a, l ; C,A => 1st Operand @@ -69,32 +70,32 @@ __MUL16: ; Mutiplies HL with the last value stored into de stack ;;__MUL16NOADD: ;; sla e ;; rl d - ;; + ;; ;; djnz __MUL16LOOP - + __MUL16_FAST: ld b, 16 ld a, d ld c, e ex de, hl 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 - + #line 31 "mul16b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/mul8.asm b/tests/functional/mul8.asm index 333507d9b..f05a242b2 100644 --- a/tests/functional/mul8.asm +++ b/tests/functional/mul8.asm @@ -55,22 +55,23 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mul8.asm" + __MUL8: ; Performs 8bit x 8bit multiplication PROC - + ;LOCAL __MUL8A LOCAL __MUL8LOOP LOCAL __MUL8B ; 1st operand (byte) in A, 2nd operand into the stack (AF) pop hl ; return address ex (sp), hl ; CALLE convention - + ;;__MUL8_FAST: ; __FASTCALL__ entry ;; ld e, a ;; ld d, 0 ;; ld l, d - ;; - ;; sla h + ;; + ;; sla h ;; jr nc, __MUL8A ;; ld l, e ;; @@ -87,27 +88,27 @@ __MUL8: ; Performs 8bit x 8bit multiplication ;; djnz __MUL8LOOP ;; ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) - + __MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry - + ld b, 8 ld l, a xor a - + __MUL8LOOP: add a, a ; a *= 2 sla l jp nc, __MUL8B add a, h - + __MUL8B: djnz __MUL8LOOP - + ret ; result = HL ENDP - + #line 46 "mul8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/mul8a.asm b/tests/functional/mul8a.asm index 2afb00062..a537b927e 100644 --- a/tests/functional/mul8a.asm +++ b/tests/functional/mul8a.asm @@ -34,22 +34,23 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mul8.asm" + __MUL8: ; Performs 8bit x 8bit multiplication PROC - + ;LOCAL __MUL8A LOCAL __MUL8LOOP LOCAL __MUL8B ; 1st operand (byte) in A, 2nd operand into the stack (AF) pop hl ; return address ex (sp), hl ; CALLE convention - + ;;__MUL8_FAST: ; __FASTCALL__ entry ;; ld e, a ;; ld d, 0 ;; ld l, d - ;; - ;; sla h + ;; + ;; sla h ;; jr nc, __MUL8A ;; ld l, e ;; @@ -66,27 +67,27 @@ __MUL8: ; Performs 8bit x 8bit multiplication ;; djnz __MUL8LOOP ;; ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) - + __MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry - + ld b, 8 ld l, a xor a - + __MUL8LOOP: add a, a ; a *= 2 sla l jp nc, __MUL8B add a, h - + __MUL8B: djnz __MUL8LOOP - + ret ; result = HL ENDP - + #line 25 "mul8a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/mul8b.asm b/tests/functional/mul8b.asm index 60a3cd94f..d32936967 100644 --- a/tests/functional/mul8b.asm +++ b/tests/functional/mul8b.asm @@ -40,22 +40,23 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mul8.asm" + __MUL8: ; Performs 8bit x 8bit multiplication PROC - + ;LOCAL __MUL8A LOCAL __MUL8LOOP LOCAL __MUL8B ; 1st operand (byte) in A, 2nd operand into the stack (AF) pop hl ; return address ex (sp), hl ; CALLE convention - + ;;__MUL8_FAST: ; __FASTCALL__ entry ;; ld e, a ;; ld d, 0 ;; ld l, d - ;; - ;; sla h + ;; + ;; sla h ;; jr nc, __MUL8A ;; ld l, e ;; @@ -72,27 +73,27 @@ __MUL8: ; Performs 8bit x 8bit multiplication ;; djnz __MUL8LOOP ;; ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) - + __MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry - + ld b, 8 ld l, a xor a - + __MUL8LOOP: add a, a ; a *= 2 sla l jp nc, __MUL8B add a, h - + __MUL8B: djnz __MUL8LOOP - + ret ; result = HL ENDP - + #line 31 "mul8b.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/mulf00.asm b/tests/functional/mulf00.asm index 5b15bd0f8..d2963f680 100644 --- a/tests/functional/mulf00.asm +++ b/tests/functional/mulf00.asm @@ -35,38 +35,40 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mulf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -82,7 +84,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "mulf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -91,25 +93,26 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __MULF: ; Multiplication call __FPSTACK_PUSH2 - + ; ------------- ROM MUL rst 28h - defb 04h ; + defb 04h ; defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 26 "mulf00.bas" #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -130,10 +133,11 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - + + #line 27 "mulf00.bas" #line 1 "storef.asm" + __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 @@ -141,7 +145,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -149,7 +153,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -161,9 +165,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 28 "mulf00.bas" - + ZXBASIC_USER_DATA: _a: DEFB 80h diff --git a/tests/functional/mulf01.asm b/tests/functional/mulf01.asm index 120adde02..5a3b95e21 100644 --- a/tests/functional/mulf01.asm +++ b/tests/functional/mulf01.asm @@ -39,38 +39,40 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mulf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -86,7 +88,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "mulf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -95,19 +97,20 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __MULF: ; Multiplication call __FPSTACK_PUSH2 - + ; ------------- ROM MUL rst 28h - defb 04h ; + defb 04h ; defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 30 "mulf01.bas" #line 1 "storef.asm" + __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 @@ -115,7 +118,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -123,7 +126,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -135,9 +138,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 31 "mulf01.bas" - + ZXBASIC_USER_DATA: _a: DEFB 80h diff --git a/tests/functional/mulf16a.asm b/tests/functional/mulf16a.asm index 7f9ca4c0e..69e370f91 100644 --- a/tests/functional/mulf16a.asm +++ b/tests/functional/mulf16a.asm @@ -36,46 +36,49 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "mulf16.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "mulf16.asm" #line 1 "_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 - - + + __MUL32_64START: push hl exx @@ -84,18 +87,18 @@ __MUL32_64START: 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: and a ; reset carry flag sbc hl,hl ; result bits 32..47 = 0 @@ -104,8 +107,8 @@ __LMUL: exx ld a,b ; mpr is b'c'ac ld b,33 ; initialize loop counter - jp __LMULSTART - + 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 @@ -114,7 +117,7 @@ __LMULLOOP: exx adc hl,de exx - + __LMULNOADD: exx rr h ; right shift upper @@ -122,7 +125,7 @@ __LMULNOADD: exx rr h rr l - + __LMULSTART: exx rr b ; right shift mpr/ @@ -131,29 +134,29 @@ __LMULSTART: rra ; equivalent to rr a rr c djnz __LMULLOOP - + ret ; result in h'l'hlb'c'ac - + #line 3 "mulf16.asm" - -__MULF16: ; + +__MULF16: ; ld a, d ; load sgn into a ex af, af' ; saves it call __ABS32 ; convert to positive - - exx + + 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 @@ -163,21 +166,21 @@ __ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 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 + 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 - + + ret + #line 27 "mulf16a.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00h diff --git a/tests/functional/newl.asm b/tests/functional/newl.asm new file mode 100644 index 000000000..fd40910d9 --- /dev/null +++ b/tests/functional/newl.asm @@ -0,0 +1,4 @@ + + + + diff --git a/tests/functional/nir.asm b/tests/functional/nir.asm index d0c7114cf..1f6155def 100644 --- a/tests/functional/nir.asm +++ b/tests/functional/nir.asm @@ -22,9 +22,8 @@ __LABEL5: xor a ld (3), a __LABEL6: - ld a, (_sprite) - inc a - ld (_sprite), a + ld hl, _sprite + inc (hl) __LABEL2: ld a, 7 ld hl, (_sprite - 1) @@ -50,7 +49,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _sprite: DEFB 00 diff --git a/tests/functional/nir.bas b/tests/functional/nir.bas old mode 100755 new mode 100644 diff --git a/tests/functional/octal.asm b/tests/functional/octal.asm index e8635a22a..9bde80d5f 100644 --- a/tests/functional/octal.asm +++ b/tests/functional/octal.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 3Dh diff --git a/tests/functional/ongoto.asm b/tests/functional/ongoto.asm new file mode 100644 index 000000000..4ce0906ed --- /dev/null +++ b/tests/functional/ongoto.asm @@ -0,0 +1,1686 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld hl, __LABEL0 + push hl + ld a, (_a) + inc a + call __ON_GOTO +__LABEL__10: + ld hl, __LABEL1 + xor a + call __PRINTSTR + call PRINT_EOL +__LABEL__20: + ld hl, __LABEL2 + xor a + call __PRINTSTR + call PRINT_EOL +__LABEL__30: + ld hl, __LABEL3 + xor a + call __PRINTSTR + call PRINT_EOL + ld hl, __LABEL4 + push hl + ld a, (_a) + add a, 2 + call __ON_GOSUB + ld hl, __LABEL5 + push hl + ld a, 1 + call __ON_GOSUB + ld hl, __LABEL6 + xor a + call __PRINTSTR + call PRINT_EOL + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL__40: + ld hl, __LABEL7 + xor a + call __PRINTSTR + call PRINT_EOL + ret +__LABEL__50: + ld hl, __LABEL8 + xor a + call __PRINTSTR + call PRINT_EOL + ret +__LABEL__60: + ld hl, __LABEL9 + xor a + call __PRINTSTR + call PRINT_EOL + ret +__LABEL__70: + ld hl, __LABEL10 + xor a + call __PRINTSTR + call PRINT_EOL + ret + ld hl, 0 + ld b, h + ld c, l + jp __END_PROGRAM +__LABEL1: + DEFW 0002h + DEFB 31h + DEFB 30h +__LABEL2: + DEFW 0002h + DEFB 32h + DEFB 30h +__LABEL3: + DEFW 0002h + DEFB 33h + DEFB 30h +__LABEL6: + DEFW 0003h + DEFB 45h + DEFB 4Eh + DEFB 44h +__LABEL7: + DEFW 0002h + DEFB 34h + DEFB 30h +__LABEL8: + DEFW 0002h + DEFB 35h + DEFB 30h +__LABEL9: + DEFW 0002h + DEFB 36h + DEFB 30h +__LABEL10: + DEFW 0002h + DEFB 37h + DEFB 30h +__LABEL0: + DEFB 3h + DEFW __LABEL__10 + DEFW __LABEL__20 + DEFW __LABEL__30 +__LABEL4: + DEFB 4h + DEFW __LABEL__40 + DEFW __LABEL__50 + DEFW __LABEL__60 + DEFW __LABEL__70 +__LABEL5: + DEFB 2h + DEFW __LABEL__50 + DEFW __LABEL__60 +#line 1 "ongoto.asm" + + ; ------------------------------------------------------ + ; Implements ON .. GOTO + ; ------------------------------------------------------ + +__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) +#line 127 "ongoto.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 128 "ongoto.bas" +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 129 "ongoto.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 01h +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/ongoto.bas b/tests/functional/ongoto.bas new file mode 100644 index 000000000..fcbd0c9f0 --- /dev/null +++ b/tests/functional/ongoto.bas @@ -0,0 +1,18 @@ + +DIM a as UBYte = 1 +ON a + 1 GOTO 10, 20, 30 + +10 PRINT "10" +20 PRINT "20" +30 PRINT "30" + +ON a + 2 GOSUB 40, 50, 60, 70 +ON 1 GOSUB 50, 60 +PRINT "END" + +END +40 PRINT "40": RETURN +50 PRINT "50": RETURN +60 PRINT "60": RETURN +70 PRINT "70": RETURN + diff --git a/tests/functional/opt1_endtest.asm b/tests/functional/opt1_endtest.asm new file mode 100644 index 000000000..c0bafc840 --- /dev/null +++ b/tests/functional/opt1_endtest.asm @@ -0,0 +1,42 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, (_N) + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + ld hl, 0 + ld b, h + ld c, l + jp __END_PROGRAM + +ZXBASIC_USER_DATA: +_N: + DEFB 39h + DEFB 30h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt1_endtest.bas b/tests/functional/opt1_endtest.bas new file mode 100644 index 000000000..21529525a --- /dev/null +++ b/tests/functional/opt1_endtest.bas @@ -0,0 +1,4 @@ + +DIM N as uinteger = 12345 +end N + diff --git a/tests/functional/opt1_nolabel.asm b/tests/functional/opt1_nolabel.asm new file mode 100644 index 000000000..f378d0b7f --- /dev/null +++ b/tests/functional/opt1_nolabel.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +#line 1 + ld hl, mygod +#line 2 + jp __LABEL__finish +mygod: + nop +#line 9 +__LABEL__finish: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt1_nolabel.bas b/tests/functional/opt1_nolabel.bas new file mode 100644 index 000000000..34153eb12 --- /dev/null +++ b/tests/functional/opt1_nolabel.bas @@ -0,0 +1,14 @@ + +ASM +ld hl, mygod +END ASM + +GOTO finish + +ASM +mygod: +nop +END ASM + +finish: + diff --git a/tests/functional/opt2_dim_at.asm b/tests/functional/opt2_dim_at.asm new file mode 100644 index 000000000..c5d15deae --- /dev/null +++ b/tests/functional/opt2_dim_at.asm @@ -0,0 +1,38 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 65000 + ld (_UDGptr), hl + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: + _UDGptr EQU 23675 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt2_dim_at.bas b/tests/functional/opt2_dim_at.bas new file mode 100644 index 000000000..d16ea846e --- /dev/null +++ b/tests/functional/opt2_dim_at.bas @@ -0,0 +1,5 @@ +REM variables with AT should never be optimized +REM they don't take space and might have collateral (desired) effects +DIM UDGptr As Uinteger AT 23675 +LET UDGptr = 65000 + diff --git a/tests/functional/opt2_ifband.asm b/tests/functional/opt2_ifband.asm new file mode 100644 index 000000000..03fef6ec3 --- /dev/null +++ b/tests/functional/opt2_ifband.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + and 1 + jp z, __LABEL1 + ld hl, _a + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt2_ifband.bas b/tests/functional/opt2_ifband.bas new file mode 100644 index 000000000..5ff40202f --- /dev/null +++ b/tests/functional/opt2_ifband.bas @@ -0,0 +1,5 @@ + +DIM a as Ubyte + +IF a bAND 1 a = a + 1 + diff --git a/tests/functional/opt2_ifbor.asm b/tests/functional/opt2_ifbor.asm new file mode 100644 index 000000000..c6a3612f4 --- /dev/null +++ b/tests/functional/opt2_ifbor.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + or 1 + jp z, __LABEL1 + ld hl, _a + inc (hl) +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt2_ifbor.bas b/tests/functional/opt2_ifbor.bas new file mode 100644 index 000000000..87bc44604 --- /dev/null +++ b/tests/functional/opt2_ifbor.bas @@ -0,0 +1,5 @@ + +DIM a as Ubyte + +IF a bOR 1 a = a + 1 + diff --git a/tests/functional/opt2_ifbor2.asm b/tests/functional/opt2_ifbor2.asm new file mode 100644 index 000000000..1513f6d9f --- /dev/null +++ b/tests/functional/opt2_ifbor2.asm @@ -0,0 +1,52 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call _x + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 +_x: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + ld a, (ix-1) + or 1 + jp z, __LABEL1 + inc (ix-1) +__LABEL1: +_x__leave: + ld sp, ix + pop ix + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt2_ifbor2.bas b/tests/functional/opt2_ifbor2.bas new file mode 100644 index 000000000..d09073de6 --- /dev/null +++ b/tests/functional/opt2_ifbor2.bas @@ -0,0 +1,7 @@ + +SUB x +DIM a as Ubyte +IF a bOR 1 THEN a = a + 1 +END SUB + +x diff --git a/tests/functional/opt2_ifbyte.asm b/tests/functional/opt2_ifbyte.asm index d35e3fa6b..858b86e0e 100644 --- a/tests/functional/opt2_ifbyte.asm +++ b/tests/functional/opt2_ifbyte.asm @@ -13,9 +13,8 @@ __START_PROGRAM: ld a, (_a) sub 64 jp nz, __LABEL1 - ld a, (_a) - inc a - ld (_a), a + ld hl, _a + inc (hl) __LABEL1: ld hl, 0 ld b, h @@ -33,7 +32,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/opt2_incdec_byte.asm b/tests/functional/opt2_incdec_byte.asm new file mode 100644 index 000000000..3653f0111 --- /dev/null +++ b/tests/functional/opt2_incdec_byte.asm @@ -0,0 +1,41 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _a + inc (hl) + ld hl, _a + dec (hl) + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt2_incdec_byte.bas b/tests/functional/opt2_incdec_byte.bas new file mode 100644 index 000000000..9bce4d4e0 --- /dev/null +++ b/tests/functional/opt2_incdec_byte.bas @@ -0,0 +1,5 @@ +DIM a as UByte + +a = a + 1 +a = a - 1 + diff --git a/tests/functional/opt2_labelinfunc.asm b/tests/functional/opt2_labelinfunc.asm index 68c63fb83..162965c75 100644 --- a/tests/functional/opt2_labelinfunc.asm +++ b/tests/functional/opt2_labelinfunc.asm @@ -37,7 +37,7 @@ _procedure__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt2_labelinfunc3.asm b/tests/functional/opt2_labelinfunc3.asm index 68c63fb83..162965c75 100644 --- a/tests/functional/opt2_labelinfunc3.asm +++ b/tests/functional/opt2_labelinfunc3.asm @@ -37,7 +37,7 @@ _procedure__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt2_labelinfunc4.asm b/tests/functional/opt2_labelinfunc4.asm index 2ab11e7b5..6737fa8d7 100644 --- a/tests/functional/opt2_labelinfunc4.asm +++ b/tests/functional/opt2_labelinfunc4.asm @@ -45,7 +45,7 @@ _procedure_subprocedure__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt2_labelinfunc5.asm b/tests/functional/opt2_labelinfunc5.asm index 2ab11e7b5..6737fa8d7 100644 --- a/tests/functional/opt2_labelinfunc5.asm +++ b/tests/functional/opt2_labelinfunc5.asm @@ -45,7 +45,7 @@ _procedure_subprocedure__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt2_letsubstr_not_used.asm b/tests/functional/opt2_letsubstr_not_used.asm new file mode 100644 index 000000000..292c88772 --- /dev/null +++ b/tests/functional/opt2_letsubstr_not_used.asm @@ -0,0 +1,35 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt2_letsubstr_not_used.bas b/tests/functional/opt2_letsubstr_not_used.bas new file mode 100644 index 000000000..2f22ae32a --- /dev/null +++ b/tests/functional/opt2_letsubstr_not_used.bas @@ -0,0 +1,3 @@ +DIM a$ +LET a$(1)="." + diff --git a/tests/functional/opt2_localu8.asm b/tests/functional/opt2_localu8.asm index 683ae8101..4d8aaaacf 100644 --- a/tests/functional/opt2_localu8.asm +++ b/tests/functional/opt2_localu8.asm @@ -47,7 +47,7 @@ _f__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt2_pstr.asm b/tests/functional/opt2_pstr.asm index 93dc8eb66..eb4aa26ec 100644 --- a/tests/functional/opt2_pstr.asm +++ b/tests/functional/opt2_pstr.asm @@ -60,9 +60,10 @@ __LABEL0: DEFB 61h DEFB 32h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -70,25 +71,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -97,40 +98,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -138,25 +140,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -165,39 +167,39 @@ __LABEL0: ; | (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 + ; 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 @@ -205,56 +207,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -263,57 +265,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -322,47 +324,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -372,15 +374,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 48 "opt2_pstr.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -388,25 +392,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -415,50 +419,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -466,12 +471,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -479,16 +485,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -500,39 +506,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -540,15 +546,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -573,14 +579,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -588,25 +594,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -615,37 +621,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 49 "opt2_pstr.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/opt2_snake_es.asm b/tests/functional/opt2_snake_es.asm new file mode 100644 index 000000000..d403db9fc --- /dev/null +++ b/tests/functional/opt2_snake_es.asm @@ -0,0 +1,382 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_cx) + add a, 2 + ld l, a + ld h, 0 + push hl + ld a, (_cy) + add a, 2 + ld l, a + ld h, 0 + push hl + ld hl, _y + call __ARRAY + ld a, (_cy) + add a, (hl) + ld (_ny), a + ld (0), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/src/zxb/trunk/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 34 "opt2_snake_es.bas" + +ZXBASIC_USER_DATA: +_ny: + DEFB 00 +_cx: + DEFB 00 +_cy: + DEFB 00 +_y: + DEFW 0001h + DEFW 000Bh + DEFB 01h + 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 + 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 + 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 + 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 + 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 + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt2_snake_es.bas b/tests/functional/opt2_snake_es.bas new file mode 100644 index 000000000..3ef583714 --- /dev/null +++ b/tests/functional/opt2_snake_es.bas @@ -0,0 +1,6 @@ +DIM y(10, 10) as UByte +DIM ny, cx, cy as UByte + +LET ny = cy + y(cy+2,cx+2) +POKE 0, ny + diff --git a/tests/functional/opt3_17.asm b/tests/functional/opt3_17.asm index 41a4f9b72..a6b1b8a78 100644 --- a/tests/functional/opt3_17.asm +++ b/tests/functional/opt3_17.asm @@ -27,7 +27,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_OPT27wws2.asm b/tests/functional/opt3_OPT27wws2.asm new file mode 100644 index 000000000..d80751e05 --- /dev/null +++ b/tests/functional/opt3_OPT27wws2.asm @@ -0,0 +1,282 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, (_n) + push hl + ld hl, _yenem + call __ARRAY + ld a, (hl) + push af + ld hl, (_n) + push hl + ld hl, _incyenem + call __ARRAY + pop af + add a, (hl) + push af + ld hl, (_n) + push hl + ld hl, _yenem + call __ARRAY + pop af + ld (hl), a + ld bc, 0 +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/src/zxb/trunk/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/src/zxb/trunk/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/src/zxb/trunk/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/src/zxb/trunk/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/src/zxb/trunk/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 35 "opt3_OPT27wws2.bas" + +ZXBASIC_USER_DATA: +_n: + DEFB 00, 00 +_yenem: + DEFW 0000h + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_incyenem: + DEFW 0000h + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt3_OPT27wws2.bas b/tests/functional/opt3_OPT27wws2.bas new file mode 100644 index 000000000..d98318c46 --- /dev/null +++ b/tests/functional/opt3_OPT27wws2.bas @@ -0,0 +1,5 @@ +DIM n as Uinteger +DIM yenem(10) as Byte +DIM incyenem(10) as Byte +yenem(n)=yenem(n)+incyenem(n) + diff --git a/tests/functional/opt3_asmexpr.asm b/tests/functional/opt3_asmexpr.asm index e163cd20c..575de22d3 100644 --- a/tests/functional/opt3_asmexpr.asm +++ b/tests/functional/opt3_asmexpr.asm @@ -27,7 +27,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_atolo1.asm b/tests/functional/opt3_atolo1.asm index 8936ab390..8b85f1f9a 100644 --- a/tests/functional/opt3_atolo1.asm +++ b/tests/functional/opt3_atolo1.asm @@ -32,7 +32,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_atolo4.asm b/tests/functional/opt3_atolo4.asm index f71dc5f6b..ad241398c 100644 --- a/tests/functional/opt3_atolo4.asm +++ b/tests/functional/opt3_atolo4.asm @@ -31,7 +31,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _kkk: DEFB 00, 00 diff --git a/tests/functional/opt3_atoloinc.asm b/tests/functional/opt3_atoloinc.asm index b8c96c169..c4d35207c 100644 --- a/tests/functional/opt3_atoloinc.asm +++ b/tests/functional/opt3_atoloinc.asm @@ -43,7 +43,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _level: DEFB 00 diff --git a/tests/functional/opt3_dark01.asm b/tests/functional/opt3_dark01.asm index 8c55f8601..2aa028339 100644 --- a/tests/functional/opt3_dark01.asm +++ b/tests/functional/opt3_dark01.asm @@ -41,7 +41,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/opt3_data2.asm b/tests/functional/opt3_data2.asm new file mode 100644 index 000000000..c11f8bb81 --- /dev/null +++ b/tests/functional/opt3_data2.asm @@ -0,0 +1,2944 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + xor a + ld (_i), a + jp __LABEL0 +__LABEL3: + ld a, 3 + call __READ + push af + ld a, (_i) + ld l, a + ld h, 0 + push hl + ld hl, _a + call __ARRAY + pop af + ld (hl), a + ld a, (_i) + ld l, a + ld h, 0 + push hl + ld hl, _a + call __ARRAY + ld a, (hl) + call __PRINTU8 + call PRINT_EOL + ld hl, _i + inc (hl) +__LABEL0: + ld a, 9 + ld hl, (_i - 1) + cp h + jp nc, __LABEL3 + ld bc, 0 +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 +___DATA__FUNCPTR__0: + ld a, (_i) + ld h, 6 + call __MUL8_FAST +___DATA__FUNCPTR__0__leave: + ret +___DATA__FUNCPTR__1: + ld a, (_a + 3) +___DATA__FUNCPTR__1__leave: + ret +___DATA__FUNCPTR__2: + ld a, (_a + 4) +___DATA__FUNCPTR__2__leave: + ret +___DATA__FUNCPTR__3: + ld a, (_a + 5) +___DATA__FUNCPTR__3__leave: + ret +___DATA__FUNCPTR__4: + ld a, (_a + 6) +___DATA__FUNCPTR__4__leave: + ret +___DATA__FUNCPTR__5: + ld a, (_a + 7) +___DATA__FUNCPTR__5__leave: + ret +__DATA__0: + DEFB 3 + DEFB 2 + DEFB 3 + DEFB 4 + DEFB 83h + DEFW ___DATA__FUNCPTR__0 + DEFB 3 + DEFB 7 + DEFB 3 + DEFB 0 +__DATA__1: + DEFB 83h + DEFW ___DATA__FUNCPTR__1 + DEFB 83h + DEFW ___DATA__FUNCPTR__2 + DEFB 83h + DEFW ___DATA__FUNCPTR__3 + DEFB 83h + DEFW ___DATA__FUNCPTR__4 + DEFB 83h + DEFW ___DATA__FUNCPTR__5 +__DATA__END: + DEFB 00h +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 97 "opt3_data2.bas" +#line 1 "mul8.asm" + +__MUL8: ; Performs 8bit x 8bit multiplication + PROC + + ;LOCAL __MUL8A + LOCAL __MUL8LOOP + LOCAL __MUL8B + ; 1st operand (byte) in A, 2nd operand into the stack (AF) + pop hl ; return address + ex (sp), hl ; CALLE convention + +;;__MUL8_FAST: ; __FASTCALL__ entry + ;; ld e, a + ;; ld d, 0 + ;; ld l, d + ;; + ;; sla h + ;; jr nc, __MUL8A + ;; ld l, e + ;; +;;__MUL8A: + ;; + ;; ld b, 7 +;;__MUL8LOOP: + ;; add hl, hl + ;; jr nc, __MUL8B + ;; + ;; add hl, de + ;; +;;__MUL8B: + ;; djnz __MUL8LOOP + ;; + ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) + +__MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry + + ld b, 8 + ld l, a + xor a + +__MUL8LOOP: + add a, a ; a *= 2 + sla l + jp nc, __MUL8B + add a, h + +__MUL8B: + djnz __MUL8LOOP + + ret ; result = HL + ENDP + +#line 98 "opt3_data2.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 99 "opt3_data2.bas" +#line 1 "printu8.asm" + +#line 1 "printi8.asm" + +#line 1 "printnum.asm" + + + + +__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 mus preserve registers + ld a, '-' + jp __PRINT_DIGIT + + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + + +#line 2 "printi8.asm" +#line 1 "div8.asm" + + ; -------------------------------- +__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 + +#line 3 "printi8.asm" + +__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 + +#line 2 "printu8.asm" + +#line 100 "opt3_data2.bas" +#line 1 "read_restore.asm" + + ;; 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 + + +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 29 "read_restore.asm" + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 101 "opt3_data2.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 +_a: + DEFW 0000h + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt3_data2.bas b/tests/functional/opt3_data2.bas new file mode 100644 index 000000000..a9e1aa7f1 --- /dev/null +++ b/tests/functional/opt3_data2.bas @@ -0,0 +1,11 @@ +DIM a(9) AS UBYTE + +FOR i = 0 TO 9: REM 0 TO 9 => 10 elements + READ a(i) + PRINT a(i) +NEXT i + +REM notice the a * a expression +DATA 2, 4, 6 * i, 7, 0 +DATA a(0), a(1), a(2), a(3), a(4) + diff --git a/tests/functional/opt3_einar.asm b/tests/functional/opt3_einar.asm index 761eff5a6..4883b26b3 100644 --- a/tests/functional/opt3_einar.asm +++ b/tests/functional/opt3_einar.asm @@ -37,9 +37,9 @@ _x2: push hl inc sp ld (ix-1), 129 - ld a, (ix-1) - neg - add a, 32 + ld a, 32 + sub (ix-1) + ccf jp nc, __LABEL0 ld hl, __LABEL2 xor a @@ -66,15 +66,17 @@ __LABEL3: DEFB 4Fh DEFB 4Bh #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -82,45 +84,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -133,41 +136,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -175,12 +180,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -188,50 +194,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -240,15 +247,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -256,26 +265,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -289,44 +298,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -337,27 +347,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -365,27 +376,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -394,81 +406,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -477,32 +491,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -512,7 +526,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -520,19 +534,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -542,7 +557,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -553,18 +568,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -574,7 +590,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -585,18 +601,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -606,7 +623,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -619,21 +636,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -642,79 +660,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -723,75 +741,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -801,7 +819,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -813,16 +831,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -836,49 +854,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -889,17 +907,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -912,27 +930,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -940,10 +958,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -960,80 +978,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1050,8 +1068,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1075,17 +1093,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1096,7 +1114,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1106,21 +1124,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1139,9 +1157,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1166,19 +1184,21 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 53 "opt3_einar.bas" #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1186,25 +1206,25 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1213,40 +1233,41 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1254,25 +1275,25 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1281,39 +1302,39 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; | (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 + ; 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 @@ -1321,56 +1342,56 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1379,57 +1400,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1438,47 +1459,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1488,51 +1509,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1541,11 +1562,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 54 "opt3_einar.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/opt3_einar04.asm b/tests/functional/opt3_einar04.asm index c2cefcb74..4112b140d 100644 --- a/tests/functional/opt3_einar04.asm +++ b/tests/functional/opt3_einar04.asm @@ -38,7 +38,7 @@ _test: #line 6 _test__leave: ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_einar05.asm b/tests/functional/opt3_einar05.asm index 4c11463a8..c5f5177c9 100644 --- a/tests/functional/opt3_einar05.asm +++ b/tests/functional/opt3_einar05.asm @@ -32,7 +32,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_einar06.asm b/tests/functional/opt3_einar06.asm index 21de05b0a..629f85e02 100644 --- a/tests/functional/opt3_einar06.asm +++ b/tests/functional/opt3_einar06.asm @@ -42,7 +42,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_endtest.asm b/tests/functional/opt3_endtest.asm new file mode 100644 index 000000000..5f8ee7ece --- /dev/null +++ b/tests/functional/opt3_endtest.asm @@ -0,0 +1,38 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld bc, (_N) +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + ld bc, 0 + jp __END_PROGRAM + +ZXBASIC_USER_DATA: +_N: + DEFB 39h + DEFB 30h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt3_endtest.bas b/tests/functional/opt3_endtest.bas new file mode 100644 index 000000000..21529525a --- /dev/null +++ b/tests/functional/opt3_endtest.bas @@ -0,0 +1,4 @@ + +DIM N as uinteger = 12345 +end N + diff --git a/tests/functional/opt3_haplo01.asm b/tests/functional/opt3_haplo01.asm index 3c7884ab2..2e3491c4b 100644 --- a/tests/functional/opt3_haplo01.asm +++ b/tests/functional/opt3_haplo01.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/opt3_haplo05.asm b/tests/functional/opt3_haplo05.asm new file mode 100644 index 000000000..3426200a7 --- /dev/null +++ b/tests/functional/opt3_haplo05.asm @@ -0,0 +1,171 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 4 + ld (31744), a + ld a, 83 + ld hl, (_dataSprite) + ld (hl), a + ld de, 11 + add hl, de + push hl + ld hl, (_dataSprite) + ld de, 4 + add hl, de + ld a, (hl) + ld h, 6 + call __MUL8_FAST + pop hl + ld (hl), a + ld hl, (_dataSprite) + ld de, 12 + add hl, de + push hl + ld hl, (_dataSprite) + ld de, 5 + add hl, de + ld a, (hl) + pop hl + add a, a + add a, a + ld (hl), a + ld hl, (_dataSprite) + ld de, 17 + add hl, de + ld de, 56978 + ld (hl), e + inc hl + ld (hl), d + ld hl, (_dataSprite) + ld de, 30 + add hl, de + push hl + ld hl, (_dataSprite) + ld de, 8 + add hl, de + ld a, (hl) + pop hl + ld (hl), a + ld hl, 0 + ld (31748), hl + ld hl, (_dataSprite) + ld de, 6 + add hl, de + xor a + ld (hl), a + ld hl, (_dataSprite) + ld de, 7 + add hl, de + xor a + ld (hl), a + ld hl, (_dataSprite) + ld de, 8 + add hl, de + xor a + ld (hl), a + ld hl, (_dataSprite) + ld de, 21 + add hl, de + xor a + ld (hl), a + ld hl, (_dataSprite) + ld de, 22 + add hl, de + xor a + ld (hl), a + ld hl, (_dataSprite) + ld de, 23 + add hl, de + xor a + ld (hl), a + ld hl, (_dataSprite) + ld de, 24 + add hl, de + xor a + ld (hl), a + ld bc, 0 +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "mul8.asm" + +__MUL8: ; Performs 8bit x 8bit multiplication + PROC + + ;LOCAL __MUL8A + LOCAL __MUL8LOOP + LOCAL __MUL8B + ; 1st operand (byte) in A, 2nd operand into the stack (AF) + pop hl ; return address + ex (sp), hl ; CALLE convention + +;;__MUL8_FAST: ; __FASTCALL__ entry + ;; ld e, a + ;; ld d, 0 + ;; ld l, d + ;; + ;; sla h + ;; jr nc, __MUL8A + ;; ld l, e + ;; +;;__MUL8A: + ;; + ;; ld b, 7 +;;__MUL8LOOP: + ;; add hl, hl + ;; jr nc, __MUL8B + ;; + ;; add hl, de + ;; +;;__MUL8B: + ;; djnz __MUL8LOOP + ;; + ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) + +__MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry + + ld b, 8 + ld l, a + xor a + +__MUL8LOOP: + add a, a ; a *= 2 + sla l + jp nc, __MUL8B + add a, h + +__MUL8B: + djnz __MUL8LOOP + + ret ; result = HL + ENDP + +#line 98 "opt3_haplo05.bas" + +ZXBASIC_USER_DATA: +_dataSprite: + DEFB 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt3_haplo05.bas b/tests/functional/opt3_haplo05.bas new file mode 100644 index 000000000..034ab1db1 --- /dev/null +++ b/tests/functional/opt3_haplo05.bas @@ -0,0 +1,28 @@ +Dim dataSprite,dw1,dw2,dw3,tB as Uinteger + +const enemiSprites as Uinteger= 49152 +const enemiBullets as Uinteger =63872 +const dirGraficoHuevo as Uinteger=enemiSprites+(91*86) +const dirGraficoBoom as Uinteger=enemiSprites+(97*86) + +const tablaSprites as Uinteger=30720 +const bulletToloStatus as UInteger= tablaSprites+1024 +const bulletToloCol as UInteger= tablaSprites+1024+4 +const bulletToloFila as UInteger= tablaSprites+1024+5 +const bulletToloFace as UInteger= tablaSprites+1024+8 + +poke bulletToloStatus,%00000100 +poke (dataSprite), %01010011 +poke (dataSprite+11),peek ((dataSprite+4))*6 +poke (dataSprite+12),peek ((dataSprite+5))*4 +poke UInteger (dataSprite+17),dirGraficoHuevo +poke (dataSprite+30),peek ((dataSprite+8)) +poke UInteger bulletToloCol,0 + +poke (dataSprite+6),0 +poke (dataSprite+7),0 +poke (dataSprite+8),0 +poke (dataSprite+21),0 +poke (dataSprite+22),0 +poke (dataSprite+23),0 +poke (dataSprite+24),0 diff --git a/tests/functional/opt3_haplo06.asm b/tests/functional/opt3_haplo06.asm new file mode 100644 index 000000000..22c9a5b0e --- /dev/null +++ b/tests/functional/opt3_haplo06.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, (_y) + ld a, (_a) + or (hl) + inc hl + or (hl) + ld (_a), a + ld bc, 0 +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 +_y: + DEFB 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt3_haplo06.bas b/tests/functional/opt3_haplo06.bas new file mode 100644 index 000000000..0d50974da --- /dev/null +++ b/tests/functional/opt3_haplo06.bas @@ -0,0 +1,8 @@ + +DIM a as UByte +DIM y as Uinteger + +a = a bOR peek(y) bOR peek(y + 1) + + + diff --git a/tests/functional/opt3_haplobug.asm b/tests/functional/opt3_haplobug.asm new file mode 100644 index 000000000..306e25366 --- /dev/null +++ b/tests/functional/opt3_haplobug.asm @@ -0,0 +1,67 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, (_dataSprite) + ld de, 26 + add hl, de + ld de, 0 + ld (hl), e + inc hl + ld (hl), d + ld hl, (_dataSprite) + ld de, 11 + add hl, de + push hl + ld hl, (_dataSprite) + ld de, 28 + add hl, de + ld a, (hl) + pop hl + ld (hl), a + ld hl, (_dataSprite) + ld de, 12 + add hl, de + push hl + ld hl, (_dataSprite) + ld de, 29 + add hl, de + ld a, (hl) + pop hl + ld (hl), a + ld hl, (_dataSprite) + ld de, 30 + add hl, de + xor a + ld (hl), a + ld bc, 0 +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_dataSprite: + DEFB 00, 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt3_haplobug.bas b/tests/functional/opt3_haplobug.bas new file mode 100644 index 000000000..3cc394018 --- /dev/null +++ b/tests/functional/opt3_haplobug.bas @@ -0,0 +1,13 @@ +#define spriteX (dataSprite+11) +#define spriteY (dataSprite+12) +#define spriteOldX (dataSprite+28) +#define spriteOldY (dataSprite+29) +#define spriteEsp0 (dataSprite+30) +#define spriteTimer (dataSprite+26) + +DIM dataSprite as Uinteger +poke UInteger spriteTimer,0 +poke spriteX, peek (spriteOldX) +poke spriteY, peek (spriteOldY) +poke spriteEsp0,0 + diff --git a/tests/functional/opt3_ifgotoelse.asm b/tests/functional/opt3_ifgotoelse.asm new file mode 100644 index 000000000..8588e538a --- /dev/null +++ b/tests/functional/opt3_ifgotoelse.asm @@ -0,0 +1,46 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, (_a) + dec a + jp nz, __LABEL0 + ld hl, _a + inc (hl) + jp __LABEL__10 +__LABEL0: + ld a, (_a) + add a, 2 + ld (_a), a +__LABEL__10: + ld bc, 0 +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt3_ifgotoelse.bas b/tests/functional/opt3_ifgotoelse.bas new file mode 100644 index 000000000..f92630f4b --- /dev/null +++ b/tests/functional/opt3_ifgotoelse.bas @@ -0,0 +1,11 @@ +DIM a as Ubyte + +IF a = 1 THEN + a = a + 1 + GOTO 10 +ELSE + a = a + 2 + GOTO 10 +END IF + +10 REM diff --git a/tests/functional/opt3_lcd5.asm b/tests/functional/opt3_lcd5.asm index 6c17d76c7..ae6cdd401 100644 --- a/tests/functional/opt3_lcd5.asm +++ b/tests/functional/opt3_lcd5.asm @@ -70,7 +70,10 @@ _ScanField: call __LTI16 ld h, a pop af - call __AND8 + or a + jr z, __LABEL6 + ld a, h +__LABEL6: push af ld l, (ix+6) ld h, (ix+7) @@ -79,7 +82,10 @@ _ScanField: call __LEI16 ld h, a pop af - call __AND8 + or a + jr z, __LABEL7 + ld a, h +__LABEL7: push af ld l, (ix+6) ld h, (ix+7) @@ -87,7 +93,10 @@ _ScanField: call __LTI16 ld h, a pop af - call __AND8 + or a + jr z, __LABEL8 + ld a, h +__LABEL8: or a jp z, __LABEL0 ld hl, __LABEL__overlay @@ -109,13 +118,10 @@ _ScanField: ld (ix-2), l ld (ix-1), h ld a, (hl) - ld h, (ix+9) - and h + and (ix+9) jp _ScanField__leave - jp __LABEL1 __LABEL0: xor a -__LABEL1: _ScanField__leave: ld sp, ix pop ix @@ -150,9 +156,8 @@ _SetField: ld (ix-1), h push hl ld a, (hl) - ld h, (ix+9) - or h pop hl + or (ix+9) ld (hl), a _SetField__leave: ld sp, ix @@ -176,12 +181,10 @@ _ScanNear: ld a, (ix+7) dec a ld l, a - ld h, 0 push hl ld a, (ix+5) dec a ld l, a - ld h, 0 push hl call _ScanField dec a @@ -206,7 +209,6 @@ _ScanNear: ld h, a pop af or h - or a jp z, __LABEL3 ld (ix-1), 1 __LABEL3: @@ -354,11 +356,9 @@ __LABEL3: ld h, a pop af or h - or a jp z, __LABEL5 ld a, (ix-1) - ld h, 32 - or h + or 32 ld (ix-1), a __LABEL5: ld a, (ix-1) @@ -371,64 +371,54 @@ _ScanNear__leave: ex (sp), hl exx ret -#line 1 "and8.asm" - ; FASTCALL boolean and 8 version. - ; result in Accumulator (0 False, not 0 True) -; __FASTCALL__ version (operands: A, H) - ; Performs 8bit and 8bit and returns the boolean - -__AND8: - or a - ret z - ld a, h - ret - -#line 363 "opt3_lcd5.bas" #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -436,64 +426,81 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - -#line 364 "opt3_lcd5.bas" + +#line 363 "opt3_lcd5.bas" #line 1 "lei16.asm" + __LEI16: PROC LOCAL checkParity @@ -510,9 +517,11 @@ checkParity: inc a ; True ret ENDP -#line 365 "opt3_lcd5.bas" +#line 364 "opt3_lcd5.bas" #line 1 "lti16.asm" + #line 1 "lei8.asm" + __LEI8: ; Signed <= comparison for 8bit int ; A <= H (registers) PROC @@ -521,10 +530,10 @@ __LEI8: ; Signed <= comparison for 8bit int jr nz, __LTI inc a ret - + __LTI8: ; Test 8 bit values A < H sub h - + __LTI: ; Generic signed comparison jp po, checkParity xor 0x80 @@ -535,7 +544,7 @@ checkParity: ret ENDP #line 2 "lti16.asm" - + __LTI16: ; Test 8 bit values HL < DE ; Returns result in A: 0 = False, !0 = True PROC @@ -551,8 +560,8 @@ checkParity: inc a ; True ret ENDP -#line 366 "opt3_lcd5.bas" - +#line 365 "opt3_lcd5.bas" + ZXBASIC_USER_DATA: _x: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/opt3_lcd6.asm b/tests/functional/opt3_lcd6.asm index c6c408cc3..7328d3db6 100644 --- a/tests/functional/opt3_lcd6.asm +++ b/tests/functional/opt3_lcd6.asm @@ -26,7 +26,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_lcd_o3_crash.asm b/tests/functional/opt3_lcd_o3_crash.asm new file mode 100644 index 000000000..5d2c2dc83 --- /dev/null +++ b/tests/functional/opt3_lcd_o3_crash.asm @@ -0,0 +1,57 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call _Drawscreen +__LABEL__Shoottable1: +__LABEL__Shoottable2: +__LABEL__UDGs01: +__LABEL__Buffer: + ld bc, 0 +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +__CALL_BACK__: + DEFW 0 +_Drawscreen: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + ld (ix-1), 0 + jp __LABEL0 +__LABEL3: + inc (ix-1) +__LABEL0: + ld a, 21 + cp (ix-1) + jp nc, __LABEL3 +_Drawscreen__leave: + ld sp, ix + pop ix + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt3_lcd_o3_crash.bas b/tests/functional/opt3_lcd_o3_crash.bas new file mode 100644 index 000000000..42bf469a2 --- /dev/null +++ b/tests/functional/opt3_lcd_o3_crash.bas @@ -0,0 +1,18 @@ +dim control$,key$ as string +dim posx,posy,dir,drw,found as byte +dim shtx,shty,shard as Uinteger + +sub Drawscreen() + dim x as ubyte + dim adr as uinteger + adr=@Buffer + for x=0 to 21 + next x +end sub + +Drawscreen() + +Shoottable1: +Shoottable2: +UDGs01: +Buffer: diff --git a/tests/functional/opt3_ldhlhl.asm b/tests/functional/opt3_ldhlhl.asm index 1aca15fb1..26d1fa621 100644 --- a/tests/functional/opt3_ldhlhl.asm +++ b/tests/functional/opt3_ldhlhl.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_proc0.asm b/tests/functional/opt3_proc0.asm index 2e27fdb0d..e02cdb921 100644 --- a/tests/functional/opt3_proc0.asm +++ b/tests/functional/opt3_proc0.asm @@ -53,7 +53,7 @@ _p__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/opt3_sp.asm b/tests/functional/opt3_sp.asm index 9234e11b2..2a1762121 100644 --- a/tests/functional/opt3_sp.asm +++ b/tests/functional/opt3_sp.asm @@ -78,38 +78,40 @@ _test__leave: exx ret #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -125,7 +127,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -134,43 +136,45 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 69 "opt3_sp.bas" #line 1 "ploadf.asm" + ; Parameter / Local var load ; A => Offset ; IX = Stack Frame ; RESULT: HL => IX + DE - + #line 1 "iloadf.asm" + ; __FASTCALL__ routine which ; loads a 40 bits floating point into A ED CB ; stored at position pointed by POINTER HL ;A DE, BC <-- ((HL)) - + __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) + ld a, (hl) inc hl ld e, (hl) inc hl @@ -180,23 +184,25 @@ __LOADF: ; Loads a 40 bits FP number from address pointed by HL inc hl ld b, (hl) ret - + #line 7 "ploadf.asm" - + __PLOADF: push ix pop hl add hl, de jp __LOADF - + #line 70 "opt3_sp.bas" #line 1 "pstoref.asm" - ; Stores FP number in A ED CB at location HL+IX - ; HL = Offset - ; IX = Stack Frame - ; A ED CB = FP Number - + + ; Stores FP number in A ED CB at location HL+IX + ; HL = Offset + ; IX = Stack Frame + ; A ED CB = FP Number + #line 1 "storef.asm" + __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 @@ -204,7 +210,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -212,7 +218,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -224,52 +230,54 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 7 "pstoref.asm" - - ; Stored a float number in A ED CB into the address pointed by IX + HL -__PSTOREF: - push de - ex de, hl ; DE <- HL - push ix - pop hl ; HL <- IX - add hl, de ; HL <- IX + DE - pop de - jp __STOREF - + + ; Stored a float number in A ED CB into the address pointed by IX + HL +__PSTOREF: + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + DE + pop de + jp __STOREF + #line 71 "opt3_sp.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -278,35 +286,35 @@ __I8TOFREG: 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 @@ -314,20 +322,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -335,32 +343,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 72 "opt3_sp.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/optconst.asm b/tests/functional/optconst.asm index 4f1540623..9eb89c718 100644 --- a/tests/functional/optconst.asm +++ b/tests/functional/optconst.asm @@ -56,15 +56,17 @@ __LABEL__label2: ld c, l jp __END_PROGRAM #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -72,45 +74,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -123,41 +126,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -165,12 +170,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -178,50 +184,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -230,15 +237,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -246,26 +255,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -279,44 +288,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -327,27 +337,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -355,27 +366,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -384,81 +396,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -467,32 +481,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -502,7 +516,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -510,19 +524,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -532,7 +547,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -543,18 +558,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -564,7 +580,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -575,18 +591,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -596,7 +613,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -609,21 +626,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -632,79 +650,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -713,75 +731,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -791,7 +809,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -803,16 +821,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -826,49 +844,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -879,17 +897,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -902,27 +920,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -930,10 +948,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -950,80 +968,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1040,8 +1058,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1065,17 +1083,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1086,7 +1104,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1096,21 +1114,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1129,9 +1147,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1156,30 +1174,33 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 46 "optconst.bas" #line 1 "printu32.asm" + #line 1 "printi32.asm" + #line 1 "printnum.asm" - - - + + + + __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 @@ -1187,53 +1208,55 @@ __PRINTU_CONT: pop bc djnz __PRINTU_CONT ret - + ENDP - - + + __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER mus preserve registers ld a, '-' jp __PRINT_DIGIT - + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - + + #line 2 "printi32.asm" #line 1 "neg32.asm" + __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 - + #line 3 "printi32.asm" #line 1 "div32.asm" - - + + + ; --------------------------------------------------------- __DIVU32: ; 32 bit unsigned division ; DEHL = Dividend, Stack Top = Divisor @@ -1245,7 +1268,7 @@ __DIVU32: ; 32 bit unsigned division 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) @@ -1261,9 +1284,9 @@ __DIVU32START: ; Performs D'E'H'L' / HLDE 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 @@ -1271,29 +1294,29 @@ __DIV32LOOP: 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 @@ -1303,34 +1326,34 @@ __DIV32NOADD: 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 + + exx pop hl pop de - + ret - - + + __DIVI32: ; 32 bit signed division ; DEHL = Dividend, Stack Top = Divisor ; A = Dividend, B = Divisor => A / B @@ -1338,76 +1361,76 @@ __DIVI32: ; 32 bit signed division 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 - + 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 - + jp __MODU32START + #line 4 "printi32.asm" - - - + + + __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) @@ -1415,13 +1438,13 @@ __PRINTU_LOOP: exx inc b jp __PRINTU_LOOP ; Uses JP in loops - + ENDP - + #line 2 "printu32.asm" - + #line 47 "optconst.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/optspeed.asm b/tests/functional/optspeed.asm new file mode 100644 index 000000000..d22ab78d9 --- /dev/null +++ b/tests/functional/optspeed.asm @@ -0,0 +1,724 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__finish: + call _choque + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_choque: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + push hl + call _choque + dec a + jp nz, __LABEL0 + ld a, (_face) + or a + jp nz, __LABEL3 + xor a + ld hl, (_ds1 - 1) + sub h + ccf + jp nc, __LABEL4 + ld a, 1 + ld (_face), a + ld (ix-1), 3 + jp __LABEL5 +__LABEL4: + ld a, 3 + ld (_face), a + ld (ix-1), 4 +__LABEL5: +__LABEL3: + jp __LABEL1 +__LABEL0: + call _choque + sub 8 + jp nz, __LABEL6 + ld a, (_face) + sub 3 + jp nz, __LABEL9 + push ix + pop hl + ld de, -6 + add hl, de + call __PLOADF + push bc + push de + push af + ld a, 000h + ld de, 00000h + ld bc, 00000h + call __LEF + or a + jp z, __LABEL10 + ld a, 2 + ld (_face), a + ld (ix-1), 4 + jp __LABEL11 +__LABEL10: + xor a + ld (_face), a + ld (ix-1), 3 +__LABEL11: +__LABEL9: + jp __LABEL7 +__LABEL6: + call _choque + sub 3 + jp nz, __LABEL12 + ld a, (_face) + or a + jp nz, __LABEL14 + ld a, 3 + ld (_face), a + ld (ix-1), 4 + jp __LABEL15 +__LABEL14: + ld a, (_face) + dec a + jp nz, __LABEL17 + ld a, 2 + ld (_face), a + ld (ix-1), 3 +__LABEL17: +__LABEL15: + jp __LABEL13 +__LABEL12: + call _choque + sub 6 + jp nz, __LABEL18 + ld a, (_face) + dec a + jp nz, __LABEL20 + xor a + ld (_face), a + ld (ix-1), 4 + jp __LABEL21 +__LABEL20: + ld a, (_face) + sub 2 + jp nz, __LABEL23 + ld a, 3 + ld (_face), a + ld (ix-1), 3 +__LABEL23: +__LABEL21: + jp __LABEL19 +__LABEL18: + call _choque + sub 12 + jp nz, __LABEL24 + ld a, (_face) + sub 2 + jp nz, __LABEL26 + ld a, 1 + ld (_face), a + ld (ix-1), 4 + jp __LABEL27 +__LABEL26: + ld a, (_face) + sub 3 + jp nz, __LABEL29 + xor a + ld (_face), a + ld (ix-1), 3 +__LABEL29: +__LABEL27: + jp __LABEL25 +__LABEL24: + call _choque + sub 9 + jp nz, __LABEL30 + ld a, (_face) + or a + jp nz, __LABEL32 + ld a, 1 + ld (_face), a + ld (ix-1), 3 + jp __LABEL33 +__LABEL32: + ld a, (_face) + sub 3 + jp nz, __LABEL35 + ld a, 2 + ld (_face), a + ld (ix-1), 4 +__LABEL35: +__LABEL33: + jp __LABEL31 +__LABEL30: + call _choque + sub 7 + jp nz, __LABEL36 + ld a, (_face) + sub 3 + jp z, __LABEL__finish +__LABEL39: + ld a, (_face) + sub 2 + jp nz, __LABEL40 + ld (ix-1), 3 + jp __LABEL41 +__LABEL40: + ld (ix-1), 4 +__LABEL41: + ld a, 3 + ld (_face), a + jp __LABEL37 +__LABEL36: + call _choque + sub 14 + jp nz, __LABEL42 + ld a, (_face) + or a + jp z, __LABEL__finish +__LABEL45: + ld a, (_face) + sub 3 + jp nz, __LABEL46 + ld (ix-1), 3 + jp __LABEL47 +__LABEL46: + ld (ix-1), 4 +__LABEL47: + xor a + ld (_face), a + jp __LABEL43 +__LABEL42: + call _choque + sub 13 + jp nz, __LABEL48 + ld a, (_face) + dec a + jp z, __LABEL__finish +__LABEL51: + ld a, (_face) + sub 2 + jp nz, __LABEL52 + ld (ix-1), 3 + jp __LABEL53 +__LABEL52: + ld (ix-1), 4 +__LABEL53: + ld a, 1 + ld (_face), a + jp __LABEL49 +__LABEL48: + call _choque + sub 11 + jp nz, __LABEL54 + ld a, (_face) + sub 2 + jp z, __LABEL__finish +__LABEL57: + ld a, (_face) + sub 3 + jp nz, __LABEL58 + ld (ix-1), 4 + jp __LABEL59 +__LABEL58: + ld (ix-1), 3 +__LABEL59: + ld a, 2 + ld (_face), a + jp __LABEL55 +__LABEL54: + call _choque + sub 5 + jp nz, __LABEL60 + ld a, (_face) + or a + jp nz, __LABEL62 + xor a + ld hl, (_ds1 - 1) + sub h + ccf + jp nc, __LABEL64 + ld a, 1 + ld (_face), a + ld (ix-1), 3 + jp __LABEL65 +__LABEL64: + ld a, 3 + ld (_face), a + ld (ix-1), 4 +__LABEL65: + jp __LABEL63 +__LABEL62: + ld a, (_face) + sub 2 + jp nz, __LABEL67 + xor a + ld hl, (_ds1 - 1) + sub h + ccf + jp nc, __LABEL68 + ld a, 1 + ld (_face), a + ld (ix-1), 4 + jp __LABEL69 +__LABEL68: + ld a, 3 + ld (_face), a + ld (ix-1), 3 +__LABEL69: +__LABEL67: +__LABEL63: + jp __LABEL61 +__LABEL60: + call _choque + sub 10 + jp nz, __LABEL71 + ld a, (_face) + dec a + jp nz, __LABEL72 + push ix + pop hl + ld de, -6 + add hl, de + call __PLOADF + push bc + push de + push af + ld a, 000h + ld de, 00000h + ld bc, 00000h + call __LEF + or a + jp z, __LABEL74 + ld a, 2 + ld (_face), a + ld (ix-1), 3 + jp __LABEL75 +__LABEL74: + xor a + ld (_face), a + ld (ix-1), 4 +__LABEL75: + jp __LABEL73 +__LABEL72: + ld a, (_face) + sub 3 + jp nz, __LABEL77 + push ix + pop hl + ld de, -6 + add hl, de + call __PLOADF + push bc + push de + push af + ld a, 000h + ld de, 00000h + ld bc, 00000h + call __LEF + or a + jp z, __LABEL78 + ld a, 2 + ld (_face), a + ld (ix-1), 4 + jp __LABEL79 +__LABEL78: + xor a + ld (_face), a + ld (ix-1), 3 +__LABEL79: +__LABEL77: +__LABEL73: +__LABEL71: +__LABEL61: +__LABEL55: +__LABEL49: +__LABEL43: +__LABEL37: +__LABEL31: +__LABEL25: +__LABEL19: +__LABEL13: +__LABEL7: +__LABEL1: +_choque__leave: + ld sp, ix + pop ix + ret +#line 1 "lef.asm" + +#line 1 "u32tofreg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "u32tofreg.asm" +__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 + +#line 2 "lef.asm" +#line 1 "ftou32reg.asm" + + + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 3 "lef.asm" +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 4 "lef.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + +__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 + +#line 361 "optspeed.bas" +#line 1 "ploadf.asm" + + ; Parameter / Local var load + ; A => Offset + ; IX = Stack Frame +; RESULT: HL => IX + DE + +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 7 "ploadf.asm" + +__PLOADF: + push ix + pop hl + add hl, de + jp __LOADF + +#line 362 "optspeed.bas" + +ZXBASIC_USER_DATA: +_face: + DEFB 00 +_modoi: + DEFB 00 +_ds1: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/optspeed.bas b/tests/functional/optspeed.bas new file mode 100644 index 000000000..9f9e00a8a --- /dev/null +++ b/tests/functional/optspeed.bas @@ -0,0 +1,163 @@ +DIM face, modoi, ds1 as Ubyte + +function choque() as UByte + if choque=1 + if face=0 then + if ds1<=0 then + face=1 ' FACE + modo=3 ' GIRA IZDA + else + face=3 + modo=4 ' GIRA DCHA + end if + end if + + elseif choque=8 + if face=3 then + if ds2<=0 then + face=2 + modo=4 + else + face=0 + modo=3 + end if + end if + + elseif choque=3 + if face=0 then + face=3 + modo=4 + 'end if + + elseif face=1 then + face=2 + modo=3 + end if + + elseif choque=6 + if face=1 then + face=0 + modo=4 + 'end if + + elseif face=2 then + face=3 + modo=3 + end if + + elseif choque=12 + if face=2 then + face=1 + modo=4 + 'end if + + elseif face=3 then + face=0 + modo=3 + end if + + elseif choque=9 + if face=0 then + face=1 + modo=3 + 'end if + + elseif face=3 then + face=2 + modo=4 + end if + + elseif choque=7 + if face=3 then + goto finish + end if + + if face=2 then + modo=3 + else + modo=4 + end if + face=3 + + elseif choque=14 + if face=0 then + goto finish + end if + + if face=3 then + modo=3 + else + modo=4 + end if + face=0 + + elseif choque=13 + if face=1 then + goto finish + end if + + if face=2 then + modo=3 + else + modo=4 + end if + + face=1 + + elseif choque=11 + if face=2 then + goto finish + end if + + if face=3 then + modo=4 + else + modo=3 + end if + + face=2 + + elseif choque=5 + + if face=0 then + if ds1<=0 then + face=1 + modo=3 + else + face=3 + modo=4 + end if + elseif face=2 then + if ds1<=0 then + face=1 + modo=4 + else + face=3 + modo=3 + end if + end if + + elseif choque=10 + + if face=1 then + if ds2<=0 then + face=2 + modo=3 + else + face=0 + modo=4 + end if + elseif face=3 then + if ds2<=0 then + face=2 + modo=4 + else + face=0 + modo=3 + end if + end if + end if +end function +finish: +choque + diff --git a/tests/functional/or16.asm b/tests/functional/or16.asm index 58c74ca07..2926de5f0 100644 --- a/tests/functional/or16.asm +++ b/tests/functional/or16.asm @@ -47,7 +47,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/or32.asm b/tests/functional/or32.asm index 236888527..86a050bfd 100644 --- a/tests/functional/or32.asm +++ b/tests/functional/or32.asm @@ -67,30 +67,31 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "or32.asm" + __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 + + pop de or d or e - - pop de + + pop de or d or e ; A = 0 only if DEHL and TOP of the stack = 0 - + jp (hl) ; Faster "Ret" - - + + #line 58 "or32.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/or8.asm b/tests/functional/or8.asm index c377cb25c..438e76117 100644 --- a/tests/functional/or8.asm +++ b/tests/functional/or8.asm @@ -40,7 +40,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/orgasm.asm b/tests/functional/orgasm.asm new file mode 100644 index 000000000..20137aebe --- /dev/null +++ b/tests/functional/orgasm.asm @@ -0,0 +1,15 @@ + org 32768 +__START_PROGRAM: + di + exx + ld hl, 0 + + ;org 32768 + halt + halt + +__END_PROGRAM: +__END_PERA: + nop +__ENDPINA: + END diff --git a/tests/functional/orgasm.bin b/tests/functional/orgasm.bin new file mode 100644 index 000000000..bee12b0d0 Binary files /dev/null and b/tests/functional/orgasm.bin differ diff --git a/tests/functional/orgbad.asm b/tests/functional/orgbad.asm new file mode 100644 index 000000000..4b06a8ef0 --- /dev/null +++ b/tests/functional/orgbad.asm @@ -0,0 +1,5 @@ +; Invalid org address +org -1 + +nop + diff --git a/tests/functional/out0.asm b/tests/functional/out0.asm index 20b13b117..2a84fc853 100644 --- a/tests/functional/out0.asm +++ b/tests/functional/out0.asm @@ -37,50 +37,53 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -88,64 +91,80 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 28 "out0.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/param0.asm b/tests/functional/param0.asm index b71915f90..4c00c87e2 100644 --- a/tests/functional/param0.asm +++ b/tests/functional/param0.asm @@ -69,9 +69,10 @@ __LABEL1: DEFW 0001h DEFB 41h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -79,25 +80,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -106,40 +107,41 @@ __LABEL1: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -147,25 +149,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -174,39 +176,39 @@ __LABEL1: ; | (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 + ; 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 @@ -214,56 +216,56 @@ __LABEL1: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -272,57 +274,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -331,47 +333,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -381,15 +383,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 56 "param0.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -397,25 +401,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -424,50 +428,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -475,12 +480,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -488,16 +494,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -509,39 +515,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -549,15 +555,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -582,14 +588,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -597,25 +603,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -624,30 +630,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -655,15 +661,17 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 57 "param0.bas" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -671,45 +679,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -722,65 +731,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -789,15 +800,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -805,26 +818,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -838,44 +851,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -886,27 +900,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -914,27 +929,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -943,81 +959,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1026,32 +1044,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1061,7 +1079,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1069,19 +1087,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1091,7 +1110,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1102,18 +1121,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1123,7 +1143,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1134,18 +1154,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1155,7 +1176,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1168,21 +1189,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1191,79 +1213,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1272,75 +1294,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1350,7 +1372,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1362,16 +1384,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1385,49 +1407,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1438,17 +1460,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1461,27 +1483,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1489,10 +1511,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1509,80 +1531,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1599,8 +1621,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1624,17 +1646,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1645,7 +1667,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1655,21 +1677,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1688,9 +1710,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1715,57 +1737,58 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 58 "param0.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1774,105 +1797,107 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 59 "param0.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -1880,50 +1905,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 60 "param0.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/param1.asm b/tests/functional/param1.asm index 4b56ce66b..540d7874b 100644 --- a/tests/functional/param1.asm +++ b/tests/functional/param1.asm @@ -61,19 +61,22 @@ __LABEL0: DEFW 0001h DEFB 61h #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -81,45 +84,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -132,41 +136,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -174,12 +180,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -187,50 +194,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -239,15 +247,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -255,26 +265,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -288,44 +298,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -336,27 +347,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -364,27 +376,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -393,81 +406,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -476,32 +491,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -511,7 +526,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -519,19 +534,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -541,7 +557,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -552,18 +568,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -573,7 +590,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -584,18 +601,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -605,7 +623,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -618,21 +636,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -641,79 +660,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -722,75 +741,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -800,7 +819,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -812,16 +831,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -835,49 +854,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -888,17 +907,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -911,27 +930,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -939,10 +958,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -959,80 +978,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1049,8 +1068,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1074,17 +1093,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1095,7 +1114,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1105,21 +1124,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1138,9 +1157,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1165,25 +1184,27 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 48 "param1.bas" #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1191,25 +1212,25 @@ PRINT_EOL_ATTR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1218,40 +1239,41 @@ PRINT_EOL_ATTR: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1259,25 +1281,25 @@ PRINT_EOL_ATTR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1286,39 +1308,39 @@ PRINT_EOL_ATTR: ; | (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 + ; 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 @@ -1326,56 +1348,56 @@ PRINT_EOL_ATTR: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1384,57 +1406,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1443,47 +1465,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1493,51 +1515,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1546,11 +1568,12 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 49 "param1.bas" #line 1 "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$) @@ -1561,13 +1584,15 @@ __PRINT_STR: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1575,25 +1600,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1602,42 +1627,43 @@ __PRINT_STR: ; | (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 + ; 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 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1645,25 +1671,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1672,40 +1698,40 @@ __PRINT_STR: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -1717,39 +1743,39 @@ __PRINT_STR: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -1757,15 +1783,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -1790,14 +1816,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -1805,23 +1831,23 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -1840,29 +1866,29 @@ __MEM_SUBTRACT: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -1871,111 +1897,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -1991,7 +2017,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -1999,44 +2025,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 50 "param1.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/param2.asm b/tests/functional/param2.asm index 4b71edcb4..1016aac03 100644 --- a/tests/functional/param2.asm +++ b/tests/functional/param2.asm @@ -69,9 +69,10 @@ __LABEL1: DEFW 0001h DEFB 41h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -79,25 +80,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -106,40 +107,41 @@ __LABEL1: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -147,25 +149,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -174,39 +176,39 @@ __LABEL1: ; | (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 + ; 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 @@ -214,56 +216,56 @@ __LABEL1: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -272,57 +274,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -331,47 +333,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -381,15 +383,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 56 "param2.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -397,25 +401,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -424,50 +428,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -475,12 +480,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -488,16 +494,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -509,39 +515,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -549,15 +555,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -582,14 +588,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -597,25 +603,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -624,30 +630,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -655,15 +661,17 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 57 "param2.bas" #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -671,45 +679,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -722,65 +731,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -789,15 +800,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -805,26 +818,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -838,44 +851,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -886,27 +900,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -914,27 +929,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -943,81 +959,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1026,32 +1044,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1061,7 +1079,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1069,19 +1087,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1091,7 +1110,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1102,18 +1121,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1123,7 +1143,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1134,18 +1154,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1155,7 +1176,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1168,21 +1189,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1191,79 +1213,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1272,75 +1294,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1350,7 +1372,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1362,16 +1384,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1385,49 +1407,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1438,17 +1460,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1461,27 +1483,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1489,10 +1511,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1509,80 +1531,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1599,8 +1621,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1624,17 +1646,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1645,7 +1667,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1655,21 +1677,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1688,9 +1710,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1715,57 +1737,58 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 58 "param2.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1774,105 +1797,107 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 59 "param2.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -1880,50 +1905,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 60 "param2.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/param3.bas b/tests/functional/param3.bas index 3ff66b4c3..598a80512 100644 --- a/tests/functional/param3.bas +++ b/tests/functional/param3.bas @@ -1,6 +1,6 @@ ' Declare a parameter of type string using $ sigil suffix -declare function test(s$) +declare function test(s$) as float function test$(s$) return s$ + "A" diff --git a/tests/functional/parambyref1.asm b/tests/functional/parambyref1.asm index 62c1b1649..0e87ec7ed 100644 --- a/tests/functional/parambyref1.asm +++ b/tests/functional/parambyref1.asm @@ -69,15 +69,17 @@ _test__leave: exx ret #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -85,45 +87,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -136,41 +139,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -178,12 +183,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -191,50 +197,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -243,15 +250,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -259,26 +268,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -292,44 +301,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -340,27 +350,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -368,27 +379,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -397,81 +409,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -480,32 +494,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -515,7 +529,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -523,19 +537,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -545,7 +560,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -556,18 +571,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -577,7 +593,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -588,18 +604,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -609,7 +626,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -622,21 +639,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -645,79 +663,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -726,75 +744,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -804,7 +822,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -816,16 +834,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -839,49 +857,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -892,17 +910,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -915,27 +933,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -943,10 +961,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -963,80 +981,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1053,8 +1071,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1078,17 +1096,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1099,7 +1117,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1109,21 +1127,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1142,9 +1160,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1169,30 +1187,33 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 59 "parambyref1.bas" #line 1 "printu16.asm" + #line 1 "printi16.asm" + #line 1 "printnum.asm" - - - + + + + __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 @@ -1200,28 +1221,30 @@ __PRINTU_CONT: pop bc djnz __PRINTU_CONT ret - + ENDP - - + + __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER mus preserve registers ld a, '-' jp __PRINT_DIGIT - + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - + + #line 2 "printi16.asm" #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -1231,23 +1254,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -1256,46 +1279,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -1303,75 +1326,75 @@ __DIVI16_FAST: 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 + + 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 - + #line 3 "printi16.asm" - - - + + + __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 - + #line 2 "printu16.asm" - + #line 60 "parambyref1.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/paramint3.asm b/tests/functional/paramint3.asm index 1832a2a9b..bbe6343ad 100644 --- a/tests/functional/paramint3.asm +++ b/tests/functional/paramint3.asm @@ -41,7 +41,7 @@ _p__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/params_implicit.asm b/tests/functional/params_implicit.asm new file mode 100644 index 000000000..f5e017a9b --- /dev/null +++ b/tests/functional/params_implicit.asm @@ -0,0 +1,104 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_x: + push ix + ld ix, 0 + add ix, sp + ld a, 083h + ld de, 00020h + ld bc, 00000h + ld hl, 4 + call __PSTOREF +_x__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + ex (sp), hl + exx + ret +#line 1 "pstoref.asm" + + ; Stores FP number in A ED CB at location HL+IX + ; HL = Offset + ; IX = Stack Frame + ; A ED CB = FP Number + +#line 1 "storef.asm" + +__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 + +#line 7 "pstoref.asm" + + ; Stored a float number in A ED CB into the address pointed by IX + HL +__PSTOREF: + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + DE + pop de + jp __STOREF + +#line 37 "params_implicit.bas" + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/params_implicit.bas b/tests/functional/params_implicit.bas new file mode 100644 index 000000000..e74f44c8c --- /dev/null +++ b/tests/functional/params_implicit.bas @@ -0,0 +1,5 @@ +REM issues a warning on y type not being explicit +SUB x(y) + y = 5 +END SUB + diff --git a/tests/functional/paramstr3.asm b/tests/functional/paramstr3.asm index 33c21952d..6443400fd 100644 --- a/tests/functional/paramstr3.asm +++ b/tests/functional/paramstr3.asm @@ -53,9 +53,10 @@ _p__leave: exx ret #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -63,25 +64,25 @@ _p__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -90,40 +91,41 @@ _p__leave: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -131,25 +133,25 @@ _p__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -158,39 +160,39 @@ _p__leave: ; | (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 + ; 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 @@ -198,56 +200,56 @@ _p__leave: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -256,57 +258,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -315,47 +317,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -365,15 +367,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 41 "paramstr3.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -381,25 +385,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -408,50 +412,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -459,12 +464,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -472,16 +478,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -493,39 +499,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -533,15 +539,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -566,14 +572,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -581,25 +587,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -608,37 +614,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 42 "paramstr3.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/paramstr4.asm b/tests/functional/paramstr4.asm index 44d681913..fd58a8f0b 100644 --- a/tests/functional/paramstr4.asm +++ b/tests/functional/paramstr4.asm @@ -77,9 +77,10 @@ _r__leave: exx ret #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -87,25 +88,25 @@ _r__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -114,40 +115,41 @@ _r__leave: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -155,25 +157,25 @@ _r__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -182,39 +184,39 @@ _r__leave: ; | (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 + ; 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 @@ -222,56 +224,56 @@ _r__leave: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -280,57 +282,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -339,47 +341,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -389,15 +391,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 65 "paramstr4.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -405,25 +409,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -432,50 +436,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -483,12 +488,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -496,16 +502,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -517,39 +523,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -557,15 +563,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -590,14 +596,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -605,25 +611,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -632,37 +638,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 66 "paramstr4.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/paramstr5.asm b/tests/functional/paramstr5.asm index 937d6dae9..5eb06698b 100644 --- a/tests/functional/paramstr5.asm +++ b/tests/functional/paramstr5.asm @@ -77,9 +77,10 @@ _p_r__leave: exx ret #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -87,25 +88,25 @@ _p_r__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -114,40 +115,41 @@ _p_r__leave: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -155,25 +157,25 @@ _p_r__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -182,39 +184,39 @@ _p_r__leave: ; | (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 + ; 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 @@ -222,56 +224,56 @@ _p_r__leave: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -280,57 +282,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -339,47 +341,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -389,15 +391,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 65 "paramstr5.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -405,25 +409,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -432,50 +436,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -483,12 +488,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -496,16 +502,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -517,39 +523,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -557,15 +563,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -590,14 +596,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -605,25 +611,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -632,37 +638,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 66 "paramstr5.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/plot.asm b/tests/functional/plot.asm index 4766d884f..7c5835b7f 100644 --- a/tests/functional/plot.asm +++ b/tests/functional/plot.asm @@ -59,50 +59,53 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -110,84 +113,102 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 50 "plot.bas" #line 1 "plot.asm" + ; 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 - + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -195,12 +216,13 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -208,7 +230,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -216,11 +238,13 @@ __STOP: ret #line 8 "plot.asm" #line 1 "in_screen.asm" + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -228,74 +252,75 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 2 "in_screen.asm" - - + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 9 "plot.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -308,48 +333,48 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 10 "plot.asm" - + 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 - + ld c, h + 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 @@ -357,7 +382,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) res 6, h ; Starts from 0 ld bc, (SCREEN_ADDR) add hl, bc ; Now current offset - + ld b, a inc b ld a, 0FEh @@ -365,7 +390,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) __PLOT_LOOP: rrca djnz __PLOT_LOOP - + ld b, a ld a, (P_FLAG) ld c, a @@ -373,18 +398,18 @@ __PLOT_LOOP: 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 - + ;; gets ATTR position with offset given in SCREEN_ADDR ld a, h rrca @@ -395,20 +420,20 @@ __PLOT_END: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR jp PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + __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 + PIXEL_ADDR EQU 22ACh COORDS EQU 5C7Dh ENDP #line 51 "plot.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/poke0.asm b/tests/functional/poke0.asm index c84eea43c..085bdd700 100644 --- a/tests/functional/poke0.asm +++ b/tests/functional/poke0.asm @@ -21,9 +21,8 @@ __LABEL3: inc hl ld (_i), hl __LABEL4: - ld a, (_j) - inc a - ld (_j), a + ld hl, _j + inc (hl) __LABEL0: ld a, 250 ld hl, (_j - 1) @@ -46,7 +45,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _i: DEFB 00h diff --git a/tests/functional/poke1.asm b/tests/functional/poke1.asm index 7e7e70132..2b9cfddc2 100644 --- a/tests/functional/poke1.asm +++ b/tests/functional/poke1.asm @@ -61,9 +61,7 @@ __LABEL3: ld (ix-3), l ld (ix-2), h __LABEL4: - ld a, (ix-1) - inc a - ld (ix-1), a + inc (ix-1) __LABEL0: ld a, (ix-1) push af @@ -76,7 +74,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: __LABEL5: DEFB 00h diff --git a/tests/functional/poke2.asm b/tests/functional/poke2.asm index c84eea43c..085bdd700 100644 --- a/tests/functional/poke2.asm +++ b/tests/functional/poke2.asm @@ -21,9 +21,8 @@ __LABEL3: inc hl ld (_i), hl __LABEL4: - ld a, (_j) - inc a - ld (_j), a + ld hl, _j + inc (hl) __LABEL0: ld a, 250 ld hl, (_j - 1) @@ -46,7 +45,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _i: DEFB 00h diff --git a/tests/functional/pokeref.asm b/tests/functional/pokeref.asm index 13a59af05..281954dd1 100644 --- a/tests/functional/pokeref.asm +++ b/tests/functional/pokeref.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/pokeref1.asm b/tests/functional/pokeref1.asm index 55bd1ad0d..6bc56657f 100644 --- a/tests/functional/pokeref1.asm +++ b/tests/functional/pokeref1.asm @@ -36,7 +36,7 @@ _test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/pokeref2.asm b/tests/functional/pokeref2.asm index 07b139ca3..ac2feab70 100644 --- a/tests/functional/pokeref2.asm +++ b/tests/functional/pokeref2.asm @@ -44,7 +44,7 @@ _printat42__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/pooky0.asm b/tests/functional/pooky0.asm index ea0eaf190..c68a6816e 100644 --- a/tests/functional/pooky0.asm +++ b/tests/functional/pooky0.asm @@ -151,38 +151,40 @@ __EXIT_FUNCTION: exx ret #line 1 "addf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -198,7 +200,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "addf.asm" - + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -207,21 +209,22 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __ADDF: ; Addition call __FPSTACK_PUSH2 - + ; ------------- ROM ADD rst 28h defb 0fh ; ADD defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 142 "pooky0.bas" #line 1 "mulf.asm" - - + + + ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -230,43 +233,45 @@ __ADDF: ; Addition ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __MULF: ; Multiplication call __FPSTACK_PUSH2 - + ; ------------- ROM MUL rst 28h - defb 04h ; + defb 04h ; defb 38h; ; END CALC - + jp __FPSTACK_POP - + #line 143 "pooky0.bas" #line 1 "ploadf.asm" + ; Parameter / Local var load ; A => Offset ; IX = Stack Frame ; RESULT: HL => IX + DE - + #line 1 "iloadf.asm" + ; __FASTCALL__ routine which ; loads a 40 bits floating point into A ED CB ; stored at position pointed by POINTER HL ;A DE, BC <-- ((HL)) - + __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) + ld a, (hl) inc hl ld e, (hl) inc hl @@ -276,74 +281,77 @@ __LOADF: ; Loads a 40 bits FP number from address pointed by HL inc hl ld b, (hl) ret - + #line 7 "ploadf.asm" - + __PLOADF: push ix pop hl add hl, de jp __LOADF - + #line 144 "pooky0.bas" #line 1 "subf.asm" - - + + + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - - + + __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 - + #line 145 "pooky0.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -352,35 +360,35 @@ __I8TOFREG: 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 @@ -388,20 +396,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -409,32 +417,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 146 "pooky0.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/prepro00.out b/tests/functional/prepro00.out index be9dadcd4..8c3dc2129 100644 --- a/tests/functional/prepro00.out +++ b/tests/functional/prepro00.out @@ -1,5 +1,5 @@ #line 1 "prepro00.bi" -#line 1 "attr.bas" +#line 1 "/src/zxb/trunk/library/attr.bas" diff --git a/tests/functional/prepro01.out b/tests/functional/prepro01.out index 9bd51eb09..b4e23e1ea 100644 --- a/tests/functional/prepro01.out +++ b/tests/functional/prepro01.out @@ -1,5 +1,5 @@ #line 1 "prepro01.bi" -#line 1 "attr.bas" +#line 1 "/src/zxb/trunk/library/attr.bas" diff --git a/tests/functional/prepro30.out b/tests/functional/prepro30.out index 7ad765a73..9bb0ba74e 100644 --- a/tests/functional/prepro30.out +++ b/tests/functional/prepro30.out @@ -1,5 +1,6 @@ #line 1 "prepro30.bi" -#line 1 "attr.bas" +#line 1 "/src/zxb/trunk/library/attr.bas" + diff --git a/tests/functional/prepro35.out b/tests/functional/prepro35.out deleted file mode 100644 index f1e50096b..000000000 --- a/tests/functional/prepro35.out +++ /dev/null @@ -1,6 +0,0 @@ -#line 1 "prepro35.bi" -asm - ld a, \ ; test - 5 -end asm - diff --git a/tests/functional/prepro70.out b/tests/functional/prepro70.out index cdae4ec69..599230c43 100644 --- a/tests/functional/prepro70.out +++ b/tests/functional/prepro70.out @@ -3,7 +3,7 @@ DIM b -#line 1 "prepro70.bi" +#line 1 "./prepro70.bi" DIM b #line 10 "./prepro70.bi" #line 6 "prepro70.bi" diff --git a/tests/functional/prepro71.bi b/tests/functional/prepro71.bi new file mode 100644 index 000000000..aece9dbd6 --- /dev/null +++ b/tests/functional/prepro71.bi @@ -0,0 +1,4 @@ +#include "alloc.bas" + +PRINT "HOLA" + diff --git a/tests/functional/prepro71.out b/tests/functional/prepro71.out new file mode 100644 index 000000000..cd20bf26d --- /dev/null +++ b/tests/functional/prepro71.out @@ -0,0 +1,204 @@ +#line 1 "prepro71.bi" +#line 1 "/src/zxb/trunk/library/alloc.bas" + + + + + + + + + + + + + +#pragma push(case_insensitive) +#pragma case_insensitive = True + + + + + + + + + + + + + + + +function FASTCALL allocate(byval n as uinteger) as uinteger + + + + + asm + ld b, h + ld c, l + jp __MEM_ALLOC + end asm +end function + + + + + + + + +sub FASTCALL deallocate(byval addr as integer) + + + + asm + jp __MEM_FREE + end asm +end sub + + + + + + + + + + + + + + + + +function FASTCALL reallocate(byval addr as uinteger, byval n as uinteger) as uinteger + + + + + + asm + ex de, hl + pop hl + ex (sp), hl + ld b, h + ld c, l + ex de, hl + jp __REALLOC + end asm +end function + + + + + + + + +function FASTCALL memavail as uInteger + asm + 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 + end asm +end function + + + + + + + +function FASTCALL maxavail as uInteger + asm + 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 + end asm +end function + + +#pragma pop(case_insensitive) + +#require "alloc.asm" +#require "free.asm" +#require "realloc.asm" + +#line 198 "/src/zxb/trunk/library/alloc.bas" + +#line 2 "prepro71.bi" + +PRINT "HOLA" + diff --git a/tests/functional/prepro72.bi b/tests/functional/prepro72.bi new file mode 100644 index 000000000..45d7a8984 --- /dev/null +++ b/tests/functional/prepro72.bi @@ -0,0 +1,9 @@ +DIM b +#ifndef __INCLUDED__ +#define __INCLUDED__ + +#include "./prepro70.bi" + +DIM a + +#endif diff --git a/tests/functional/prepro72.out b/tests/functional/prepro72.out new file mode 100644 index 000000000..3ba0b5f2a --- /dev/null +++ b/tests/functional/prepro72.out @@ -0,0 +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" diff --git a/tests/functional/prepro73.bi b/tests/functional/prepro73.bi new file mode 100644 index 000000000..8a9862324 --- /dev/null +++ b/tests/functional/prepro73.bi @@ -0,0 +1,7 @@ +#include + +#ifndef __D7_BAS__ +#define __D7_BAS__ +#include "prepro73.bi" +#endif + diff --git a/tests/functional/prepro73.out b/tests/functional/prepro73.out new file mode 100644 index 000000000..802529748 --- /dev/null +++ b/tests/functional/prepro73.out @@ -0,0 +1,69 @@ +#line 1 "prepro73.bi" +#line 1 "/src/zxb/trunk/library/pos.bas" + + + + + + + + + + + + + + + +#pragma push(case_insensitive) +#pragma case_insensitive = true + + + + + + + + + +function FASTCALL pos as ubyte + asm + PROC + + call __LOAD_S_POSN + ld a, e + + ENDP + end asm +end function + +#pragma pop(case_insensitive) +#require "sposn.asm" + + +#line 43 "/src/zxb/trunk/library/pos.bas" + +#line 2 "prepro73.bi" + + + +#line 1 "prepro73.bi" +#line 1 "/src/zxb/trunk/library/pos.bas" + + + + + + + + + +#line 43 "/src/zxb/trunk/library/pos.bas" + +#line 2 "prepro73.bi" + +#line 7 "prepro73.bi" + +#line 6 "prepro73.bi" +#line 7 "prepro73.bi" + diff --git a/tests/functional/prepro74.bi b/tests/functional/prepro74.bi new file mode 100644 index 000000000..6e9f255d3 --- /dev/null +++ b/tests/functional/prepro74.bi @@ -0,0 +1,9 @@ +#include once + +#ifndef __D7_BAS__ +#define __D7_BAS__ +#include "prepro74.bi" +#endif + +glibberish + diff --git a/tests/functional/prepro74.out b/tests/functional/prepro74.out new file mode 100644 index 000000000..5c67a4d1e --- /dev/null +++ b/tests/functional/prepro74.out @@ -0,0 +1,61 @@ +#line 1 "prepro74.bi" +#line 1 "/src/zxb/trunk/library/pos.bas" + + + + + + + + + + + + + + + +#pragma push(case_insensitive) +#pragma case_insensitive = true + + + + + + + + + +function FASTCALL pos as ubyte + asm + PROC + + call __LOAD_S_POSN + ld a, e + + ENDP + end asm +end function + +#pragma pop(case_insensitive) +#require "sposn.asm" + + +#line 43 "/src/zxb/trunk/library/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/prepro75.bi b/tests/functional/prepro75.bi new file mode 100644 index 000000000..e55313467 --- /dev/null +++ b/tests/functional/prepro75.bi @@ -0,0 +1,6 @@ +A = 0 +#ifndef __TEST__ +#define __TEST__ +#include once "prepro75.bi" +#endif + diff --git a/tests/functional/prepro75.out b/tests/functional/prepro75.out new file mode 100644 index 000000000..cb2ba7e75 --- /dev/null +++ b/tests/functional/prepro75.out @@ -0,0 +1,11 @@ +#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/prepro76.bi b/tests/functional/prepro76.bi new file mode 100644 index 000000000..35cb2a60b --- /dev/null +++ b/tests/functional/prepro76.bi @@ -0,0 +1,3 @@ + +#error this is an intended error + diff --git a/tests/functional/prepro77.bi b/tests/functional/prepro77.bi new file mode 100644 index 000000000..15fbcdecc --- /dev/null +++ b/tests/functional/prepro77.bi @@ -0,0 +1,3 @@ + +#warning this is an intended warning + diff --git a/tests/functional/prepro77.out b/tests/functional/prepro77.out new file mode 100644 index 000000000..252ff806c --- /dev/null +++ b/tests/functional/prepro77.out @@ -0,0 +1,3 @@ +#line 1 "prepro77.bi" + + diff --git a/tests/functional/prepro80.bi b/tests/functional/prepro80.bi new file mode 100644 index 000000000..ee3c621b7 --- /dev/null +++ b/tests/functional/prepro80.bi @@ -0,0 +1,11 @@ +REM simple WaitRetrace macro + +#ifndef waitretrace +#define waitretrace 'REM Retrace \ + asm \ + halt \ + halt \ + end asm + +#endif + diff --git a/tests/functional/prepro80.out b/tests/functional/prepro80.out new file mode 100644 index 000000000..c2855c979 --- /dev/null +++ b/tests/functional/prepro80.out @@ -0,0 +1,12 @@ +#line 1 "prepro80.bi" + + + + + asm + halt + halt + end asm + +#line 11 "prepro80.bi" + diff --git a/tests/functional/preprocerr1.asm b/tests/functional/preprocerr1.asm new file mode 100644 index 000000000..cc839fee4 --- /dev/null +++ b/tests/functional/preprocerr1.asm @@ -0,0 +1,2 @@ +#include + diff --git a/tests/functional/preprocerr2.asm b/tests/functional/preprocerr2.asm new file mode 100644 index 000000000..1157a8e89 --- /dev/null +++ b/tests/functional/preprocerr2.asm @@ -0,0 +1,2 @@ +#define @ + diff --git a/tests/functional/print.asm b/tests/functional/print.asm index 89465b83e..809d74e8c 100644 --- a/tests/functional/print.asm +++ b/tests/functional/print.asm @@ -65,15 +65,17 @@ __LABEL0: DEFW 0001h DEFB 33h #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -81,45 +83,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -132,41 +135,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -174,12 +179,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -187,50 +193,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -239,15 +246,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -255,26 +264,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -288,44 +297,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -336,27 +346,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -364,27 +375,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -393,81 +405,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -476,32 +490,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -511,7 +525,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -519,19 +533,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -541,7 +556,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -552,18 +567,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -573,7 +589,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -584,18 +600,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -605,7 +622,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -618,21 +635,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -641,79 +659,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -722,75 +740,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -800,7 +818,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -812,16 +830,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -835,49 +853,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -888,17 +906,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -911,27 +929,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -939,10 +957,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -959,80 +977,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1049,8 +1067,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1074,17 +1092,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1095,7 +1113,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1105,21 +1123,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1138,9 +1156,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1165,31 +1183,35 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 52 "print.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - - - - + + + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 53 "print.bas" #line 1 "printf.asm" + #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1197,25 +1219,25 @@ PRINT_EOL_ATTR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1224,40 +1246,41 @@ PRINT_EOL_ATTR: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1265,25 +1288,25 @@ PRINT_EOL_ATTR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1292,39 +1315,39 @@ PRINT_EOL_ATTR: ; | (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 + ; 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 @@ -1332,56 +1355,56 @@ PRINT_EOL_ATTR: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1390,57 +1413,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1449,47 +1472,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1499,51 +1522,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1552,42 +1575,43 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 2 "printf.asm" #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -1603,66 +1627,68 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 3 "printf.asm" - - + + __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 + pop bc inc bc - + jp RECLAIM2 ; Frees TMP Memory - + RECLAIM2 EQU 19E8h - + ENDP - + #line 54 "print.bas" #line 1 "printf16.asm" + #line 1 "printnum.asm" - - - + + + + __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 @@ -1670,30 +1696,33 @@ __PRINTU_CONT: pop bc djnz __PRINTU_CONT ret - + ENDP - - + + __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER mus preserve registers ld a, '-' jp __PRINT_DIGIT - + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - + + #line 2 "printf16.asm" #line 1 "printi16.asm" - + + #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -1703,23 +1732,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -1728,46 +1757,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -1775,206 +1804,209 @@ __DIVI16_FAST: 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 + + 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 - + #line 3 "printi16.asm" - - - + + + __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 - + #line 3 "printf16.asm" #line 1 "neg32.asm" + __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 - + #line 4 "printf16.asm" - + __PRINTF16: ; Prints a 32bit 16.16 fixed point number PROC - + LOCAL __PRINT_FIX_LOOP LOCAL __PRINTF16_2 - - bit 7, d + + 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 ; + add hl, hl ; adc a, a ; AHL = AHL * 2 (= X * 2) - add hl, hl ; + add hl, hl ; adc a, a ; AHL = AHL * 2 (= X * 4) - - add hl, de ; + + 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 - + #line 55 "print.bas" - + #line 1 "printi8.asm" - + + #line 1 "div8.asm" + ; -------------------------------- -__DIVU8: ; 8 bit unsigned integer division +__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 + sla h + rla + cp l jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: + sub l + inc h + +__DIV8NOSUB: djnz __DIV8LOOP - + ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - + 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 @@ -1982,95 +2014,99 @@ __DIV8A: 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 - + 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 - + #line 3 "printi8.asm" - + __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 - + #line 57 "print.bas" - + #line 1 "printu16.asm" - - + + + #line 59 "print.bas" #line 1 "printu8.asm" - - + + + #line 60 "print.bas" #line 1 "strcat.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -2078,25 +2114,25 @@ __PRINTU_LOOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -2105,40 +2141,40 @@ __PRINTU_LOOP: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -2150,39 +2186,39 @@ __PRINTU_LOOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -2190,15 +2226,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -2223,14 +2259,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -2238,113 +2274,114 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "strcat.asm" #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -2352,50 +2389,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 61 "print.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/print42.asm b/tests/functional/print42.asm index 1d8724881..f44c50e50 100644 --- a/tests/functional/print42.asm +++ b/tests/functional/print42.asm @@ -50,17 +50,17 @@ _print42: push ix ld ix, 0 add ix, sp -#line 23 +#line 21 PROC LD A, H OR L - RET Z + JP Z, print42end LD C,(HL) INC HL LD B,(HL) LD A, C OR B - JP Z, print64end + JP Z, print42end INC HL LOCAL examineChar examineChar: @@ -75,22 +75,42 @@ isAt: LD HL, -2 ADD HL, BC EX DE,HL - JP NC, print64end + JP NC, print42end INC HL LD D,(HL) DEC BC INC HL LD E,(HL) DEC BC - CALL nxtchar - JR newline + ld (xycoords), de + JR nextChar LOCAL isNewline isNewline: CP 13 - JR NZ,checkvalid + JR NZ, checkdel LOCAL newline newline: + ld de, (xycoords) CALL nxtline + 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: @@ -110,7 +130,7 @@ nextChar: LD A,B OR C JR NZ, examineChar - JP print64end + JP print42end LOCAL printachar printachar: EXX @@ -287,9 +307,9 @@ ycoord: ret c ld d, 0 ret -#line 259 +#line 277 __LABEL__printAt42Coords: -#line 314 +#line 329 LOCAL xycoords xycoords: defb 0 @@ -369,7 +389,7 @@ whichcolumn: defb 252 defb 240 defb 252 - defb 240 + defb 6 defb 240 defb 255 defb 128 @@ -452,10 +472,18 @@ characters: defb 180 defb 72 defb 48 - LOCAL print64end -print64end: + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0xFC + LOCAL print42end +print42end: ENDP -#line 479 +#line 502 _print42__leave: ex af, af' exx @@ -472,9 +500,10 @@ _print42__leave: exx ret #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -482,25 +511,25 @@ _print42__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -509,40 +538,41 @@ _print42__leave: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -550,25 +580,25 @@ _print42__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -577,39 +607,39 @@ _print42__leave: ; | (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 + ; 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 @@ -617,56 +647,56 @@ _print42__leave: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -675,57 +705,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -734,47 +764,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -784,11 +814,11 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - -#line 460 "print42.bas" - + +#line 488 "print42.bas" + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/print64.asm b/tests/functional/print64.asm index 833f22ebe..a0d95b5f7 100644 --- a/tests/functional/print64.asm +++ b/tests/functional/print64.asm @@ -50,7 +50,7 @@ _print64: push ix ld ix, 0 add ix, sp -#line 22 +#line 20 PROC LD L,(IX+4) LD H,(IX+5) @@ -224,9 +224,9 @@ p64_test_Y: ret c ld d, 0 ret -#line 195 +#line 193 __LABEL__p64coords: -#line 224 +#line 222 LOCAL p64_coords p64_coords: defb 64 @@ -284,7 +284,7 @@ p64_charset: LOCAL p64_END p64_END: ENDP -#line 281 +#line 279 _print64__leave: ex af, af' exx @@ -301,9 +301,10 @@ _print64__leave: exx ret #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -311,25 +312,25 @@ _print64__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -338,40 +339,41 @@ _print64__leave: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -379,25 +381,25 @@ _print64__leave: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -406,39 +408,39 @@ _print64__leave: ; | (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 + ; 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 @@ -446,56 +448,56 @@ _print64__leave: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -504,57 +506,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -563,47 +565,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -613,11 +615,11 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 289 "print64.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/print_arrstr.asm b/tests/functional/print_arrstr.asm new file mode 100644 index 000000000..51eba47a9 --- /dev/null +++ b/tests/functional/print_arrstr.asm @@ -0,0 +1,2122 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld de, __LABEL0 + ld hl, _a + 13 + call __STORE_STR + ld hl, (_a + 13) + xor a + call __PRINTSTR + call PRINT_EOL + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 000Bh + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh + DEFB 20h + DEFB 57h + DEFB 4Fh + DEFB 52h + DEFB 4Ch + DEFB 44h +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "alloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 38 "print_arrstr.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + + + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 39 "print_arrstr.bas" +#line 1 "printstr.asm" + + + + +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 40 "print_arrstr.bas" +#line 1 "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 "strcpy.asm" + +#line 1 "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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 41 "print_arrstr.bas" + +ZXBASIC_USER_DATA: +_a: + DEFW 0000h + DEFB 02h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/print_arrstr.bas b/tests/functional/print_arrstr.bas new file mode 100644 index 000000000..233c4a498 --- /dev/null +++ b/tests/functional/print_arrstr.bas @@ -0,0 +1,5 @@ +DIM a$(10) +LET a$(5) = "HELLO WORLD" +PRINT a$(5) + + diff --git a/tests/functional/randomize.asm b/tests/functional/randomize.asm index a6ff7ae9e..298dcf688 100644 --- a/tests/functional/randomize.asm +++ b/tests/functional/randomize.asm @@ -33,118 +33,27 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "random.asm" + ; RANDOM functions - -#line 1 "mul32.asm" -#line 1 "_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 - - -__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: - and a ; reset carry flag - sbc hl,hl ; result bits 32..47 = 0 - exx - sbc hl,hl ; result bits 48..63 = 0 - 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 - -#line 2 "mul32.asm" - -__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 - - -#line 4 "random.asm" - + 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) @@ -152,18 +61,18 @@ TAKE_FRAMES: ld hl, (FRAMES + 2) ld (RANDOM_SEED_HIGH), hl ret - + FRAMES EQU 23672 ENDP - + RANDOM_SEED_HIGH EQU RAND+6 ; RANDOM seed, 16 higher bits RANDOM_SEED_LOW EQU 23670 ; RANDOM seed, 16 lower bits - - + + RAND: PROC LOCAL RAND_LOOP - ld b, 4 + ld b, 4 RAND_LOOP: ld hl,(RANDOM_SEED_LOW) ; xz -> yw ld de,0C0DEh ; yw -> zt @@ -195,30 +104,30 @@ RAND_LOOP: ld h, a 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: @@ -228,22 +137,22 @@ RND_LOOP: 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 d rr c rr b - + ld e, a ; E must have the highest byte ld a, l ; exponent in A ret - + ENDP - + #line 24 "randomize.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/read.asm b/tests/functional/read.asm new file mode 100644 index 000000000..cba30ec0c --- /dev/null +++ b/tests/functional/read.asm @@ -0,0 +1,1409 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, __DATA__0 + call __RESTORE + ld a, 9 + call __READ + ld hl, _a + call __STOREF +__LABEL__pera: +__LABEL__pina: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +___DATA__FUNCPTR__0: + ld hl, __LABEL0 + call __LOADSTR + jp ___DATA__FUNCPTR__0__leave +___DATA__FUNCPTR__0__leave: + ret +__DATA__0: + DEFB 3 + DEFB 20 + DEFB 3 + DEFB 20 + DEFB 3 + DEFB 30 +__DATA__1: + DEFB 81h + DEFW ___DATA__FUNCPTR__0 +__DATA__END: + DEFB 00h +__LABEL0: + DEFW 0004h + DEFB 4Ah + DEFB 75h + DEFB 61h + DEFB 6Eh +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "alloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 50 "read.bas" +#line 1 "read_restore.asm" + + ;; 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 + + + +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 29 "read_restore.asm" + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 51 "read.bas" +#line 1 "storef.asm" + +__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 + +#line 52 "read.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00, 00, 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/read.bas b/tests/functional/read.bas new file mode 100644 index 000000000..7c98cddd4 --- /dev/null +++ b/tests/functional/read.bas @@ -0,0 +1,12 @@ + + +RESTORE pera + +READ a + +pera: +DATA 10 * 2, 20, 30 + +pina: +DATA "Juan" + diff --git a/tests/functional/read0.bas b/tests/functional/read0.bas new file mode 100644 index 000000000..9431c6b65 --- /dev/null +++ b/tests/functional/read0.bas @@ -0,0 +1,14 @@ +REM Error x is not a var + +DIM v as Float = 1 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2 + +SUB x +END SUB + +READ x + + diff --git a/tests/functional/read1.bas b/tests/functional/read1.bas new file mode 100644 index 000000000..eccaf62fd --- /dev/null +++ b/tests/functional/read1.bas @@ -0,0 +1,13 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2 + +DIM x(5) as UByte + +READ x + + diff --git a/tests/functional/read10.asm b/tests/functional/read10.asm new file mode 100644 index 000000000..63b5422f5 --- /dev/null +++ b/tests/functional/read10.asm @@ -0,0 +1,2836 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld hl, __DATA__0 + call __RESTORE + call _p + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_p: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + push hl + ld (ix-1), 0 + jp __LABEL0 +__LABEL3: + ld a, 9 + call __READ + ld hl, -6 + call __PSTOREF + push ix + pop hl + ld de, -6 + add hl, de + call __PLOADF + call __PRINTF + call PRINT_EOL +__LABEL4: + inc (ix-1) +__LABEL0: + ld a, (ix-1) + push af + ld a, 3 + pop hl + cp h + jp nc, __LABEL3 +__LABEL2: +_p__leave: + ld sp, ix + pop ix + ret +___DATA__FUNCPTR__0: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 00000h + push hl + ld hl, 00048h + push hl + ld h, 085h + push hl + call __MULF + jp ___DATA__FUNCPTR__0__leave +___DATA__FUNCPTR__0__leave: + ret +___DATA__FUNCPTR__1: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call SIN + push bc + push de + push af + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call TAN + push bc + push de + push af + ld a, 082h + ld de, 00000h + ld bc, 00000h + call __POW + call __MULF + jp ___DATA__FUNCPTR__1__leave +___DATA__FUNCPTR__1__leave: + ret +___DATA__FUNCPTR__2: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 0A2DAh + push hl + ld hl, 00F49h + push hl + ld h, 082h + push hl + call __MULF + jp ___DATA__FUNCPTR__2__leave +___DATA__FUNCPTR__2__leave: + ret +__DATA__0: + DEFB 3 + DEFB 10 + DEFB 89h + DEFW ___DATA__FUNCPTR__0 + DEFB 89h + DEFW ___DATA__FUNCPTR__1 + DEFB 89h + DEFW ___DATA__FUNCPTR__2 +__DATA__END: + DEFB 00h +#line 1 "mulf.asm" + +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 2 "mulf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + + jp __FPSTACK_POP + +#line 119 "read10.bas" +#line 1 "ploadf.asm" + + ; Parameter / Local var load + ; A => Offset + ; IX = Stack Frame +; RESULT: HL => IX + DE + +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 7 "ploadf.asm" + +__PLOADF: + push ix + pop hl + add hl, de + jp __LOADF + +#line 120 "read10.bas" +#line 1 "pow.asm" + + + + ; ------------------------------------------------------------- + ; 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 + ; +; Operands comes swapped: + ; 1 st parameter is the BASE (A ED CB) + ; 2 nd parameter (Top of the stack) is Exponent + ; ------------------------------------------------------------- + +__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 + +#line 121 "read10.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 122 "read10.bas" +#line 1 "printf.asm" + +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 2 "printf.asm" + + + +__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 + +#line 123 "read10.bas" +#line 1 "pstoref.asm" + + ; Stores FP number in A ED CB at location HL+IX + ; HL = Offset + ; IX = Stack Frame + ; A ED CB = FP Number + +#line 1 "storef.asm" + +__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 + +#line 7 "pstoref.asm" + + ; Stored a float number in A ED CB into the address pointed by IX + HL +__PSTOREF: + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + DE + pop de + jp __STOREF + +#line 124 "read10.bas" +#line 1 "read_restore.asm" + + ;; 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 + + +#line 1 "loadstr.asm" + +#line 1 "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 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 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. + + + + + + ; --------------------------------------------------------------------- + ; 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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" + +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" + + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 125 "read10.bas" +#line 1 "sin.asm" + + + +SIN: ; Computes SIN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 1Fh + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 126 "read10.bas" +#line 1 "tan.asm" + + + +TAN: ; Computes TAN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 21h ; TAN + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 127 "read10.bas" + +ZXBASIC_USER_DATA: +_v: + DEFB 81h + DEFB 40h + DEFB 00h + DEFB 00h + DEFB 00h +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/read10.bas b/tests/functional/read10.bas new file mode 100644 index 000000000..d4e4a225c --- /dev/null +++ b/tests/functional/read10.bas @@ -0,0 +1,21 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1.5 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, PI * v + + +function p() + DIM c as Float + FOR i = 0 TO 3: + READ c + PRINT c + NEXT i +end function +p() + + + + diff --git a/tests/functional/read11.bas b/tests/functional/read11.bas new file mode 100644 index 000000000..494aece19 --- /dev/null +++ b/tests/functional/read11.bas @@ -0,0 +1,4 @@ +1 BORDER 0: PAPER 0: INK 9: BRIGHT 1: CLS +10 LET spheres=2: IF spheres THEN DIM c(spheres,3): DIM r(spheres): DIM q(spheres) +20 FOR k=1 TO spheres: READ c(k,1),c(k,2),c(k,3),r: LET r(k)=r: LET q(k)=r*r: NEXT k + diff --git a/tests/functional/read12.asm b/tests/functional/read12.asm new file mode 100644 index 000000000..fe10fb682 --- /dev/null +++ b/tests/functional/read12.asm @@ -0,0 +1,2630 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld hl, __DATA__1 + call __RESTORE + ld a, 2 + call __READ + ld (_a), a + call __PRINTI8 + call PRINT_EOL + ld a, 2 + call __READ + ld (_a), a + call __PRINTI8 + call PRINT_EOL +__LABEL__test: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +___DATA__FUNCPTR__0: + ld hl, __LABEL0 + call __LOADSTR + jp ___DATA__FUNCPTR__0__leave +___DATA__FUNCPTR__0__leave: + ret +__DATA__0: + DEFB 81h + DEFW ___DATA__FUNCPTR__0 +__DATA__1: + DEFB 3 + DEFB 1 + DEFB 3 + DEFB 2 +__DATA__END: + DEFB 00h +__LABEL0: + DEFW 0004h + DEFB 68h + DEFB 6Fh + DEFB 6Ch + DEFB 61h +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "alloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 53 "read12.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + + + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 54 "read12.bas" +#line 1 "printi8.asm" + +#line 1 "printnum.asm" + + + + +__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 mus preserve registers + ld a, '-' + jp __PRINT_DIGIT + + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + + +#line 2 "printi8.asm" +#line 1 "div8.asm" + + ; -------------------------------- +__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 + +#line 3 "printi8.asm" + +__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 + +#line 55 "read12.bas" +#line 1 "read_restore.asm" + + ;; 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 + + + +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 29 "read_restore.asm" + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 56 "read12.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/read12.bas b/tests/functional/read12.bas new file mode 100644 index 000000000..0435712a6 --- /dev/null +++ b/tests/functional/read12.bas @@ -0,0 +1,14 @@ + +RESTORE test + +DIM a as Byte + +READ a +print a +read a +print a + + DATA "hola" + +test: DATA 1, 2 + diff --git a/tests/functional/read2.bas b/tests/functional/read2.bas new file mode 100644 index 000000000..b04721a61 --- /dev/null +++ b/tests/functional/read2.bas @@ -0,0 +1,13 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1 +DIM x(3) + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, "Hello" + + +READ x + + diff --git a/tests/functional/read3.bas b/tests/functional/read3.bas new file mode 100644 index 000000000..194f9ee08 --- /dev/null +++ b/tests/functional/read3.bas @@ -0,0 +1,12 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, "Hello" + +READ x(5) + + + diff --git a/tests/functional/read4.asm b/tests/functional/read4.asm new file mode 100644 index 000000000..1b9ac7d58 --- /dev/null +++ b/tests/functional/read4.asm @@ -0,0 +1,1648 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, __DATA__0 + call __RESTORE + ld hl, _v + 4 + call __FP_PUSH_REV + ld a, 082h + ld de, 00040h + ld bc, 00000h + call __MULF + ld hl, _x + 13 + call __STOREF + ld a, 9 + call __READ + ld hl, _x + 13 + call __STOREF + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +___DATA__FUNCPTR__0: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 00000h + push hl + ld hl, 00048h + push hl + ld h, 085h + push hl + call __MULF + jp ___DATA__FUNCPTR__0__leave +___DATA__FUNCPTR__0__leave: + ret +___DATA__FUNCPTR__1: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call SIN + push bc + push de + push af + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call TAN + push bc + push de + push af + ld a, 082h + ld de, 00000h + ld bc, 00000h + call __POW + call __MULF + jp ___DATA__FUNCPTR__1__leave +___DATA__FUNCPTR__1__leave: + ret +___DATA__FUNCPTR__2: + ld hl, __LABEL0 + call __LOADSTR + jp ___DATA__FUNCPTR__2__leave +___DATA__FUNCPTR__2__leave: + ret +__DATA__0: + DEFB 3 + DEFB 10 + DEFB 89h + DEFW ___DATA__FUNCPTR__0 + DEFB 89h + DEFW ___DATA__FUNCPTR__1 + DEFB 81h + DEFW ___DATA__FUNCPTR__2 +__DATA__END: + DEFB 00h +__LABEL0: + DEFW 0005h + DEFB 48h + DEFB 65h + DEFB 6Ch + DEFB 6Ch + DEFB 6Fh +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "alloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 93 "read4.bas" +#line 1 "mulf.asm" + +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 2 "mulf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + + jp __FPSTACK_POP + +#line 94 "read4.bas" +#line 1 "pow.asm" + + + + ; ------------------------------------------------------------- + ; 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 + ; +; Operands comes swapped: + ; 1 st parameter is the BASE (A ED CB) + ; 2 nd parameter (Top of the stack) is Exponent + ; ------------------------------------------------------------- + +__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 + +#line 95 "read4.bas" +#line 1 "pushf.asm" + + + ; 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 + +__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 + + +#line 96 "read4.bas" +#line 1 "read_restore.asm" + + ;; 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 + + + +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 29 "read_restore.asm" + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 97 "read4.bas" +#line 1 "sin.asm" + + + +SIN: ; Computes SIN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 1Fh + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 98 "read4.bas" +#line 1 "storef.asm" + +__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 + +#line 99 "read4.bas" +#line 1 "tan.asm" + + + +TAN: ; Computes TAN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 21h ; TAN + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 100 "read4.bas" + +ZXBASIC_USER_DATA: +_v: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_x: + DEFW 0000h + DEFB 05h + 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 + DEFB 00h + DEFB 00h + DEFB 00h +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/read4.bas b/tests/functional/read4.bas new file mode 100644 index 000000000..d4b2f44ab --- /dev/null +++ b/tests/functional/read4.bas @@ -0,0 +1,15 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, "Hello" + + +DIM x(4) as Float +LET x(2) = v * 3 +READ x(2) + + + diff --git a/tests/functional/read5.asm b/tests/functional/read5.asm new file mode 100644 index 000000000..9b0a02e7d --- /dev/null +++ b/tests/functional/read5.asm @@ -0,0 +1,2801 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld hl, __DATA__0 + call __RESTORE + ld a, 1 + ld (_i), a + jp __LABEL0 +__LABEL3: + ld a, 9 + call __READ + ld hl, _c + call __STOREF + ld a, 9 + call __READ + ld hl, _d + call __STOREF + ld a, (_c) + ld de, (_c + 1) + ld bc, (_c + 3) + call __PRINTF + call PRINT_COMMA + ld a, (_d) + ld de, (_d + 1) + ld bc, (_d + 3) + call __PRINTF + call PRINT_EOL +__LABEL4: + ld hl, _i + inc (hl) +__LABEL0: + ld a, 4 + ld hl, (_i - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +___DATA__FUNCPTR__0: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 00000h + push hl + ld hl, 00048h + push hl + ld h, 085h + push hl + call __MULF + jp ___DATA__FUNCPTR__0__leave +___DATA__FUNCPTR__0__leave: + ret +___DATA__FUNCPTR__1: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call SIN + push bc + push de + push af + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call TAN + push bc + push de + push af + ld a, 082h + ld de, 00000h + ld bc, 00000h + call __POW + call __MULF + jp ___DATA__FUNCPTR__1__leave +___DATA__FUNCPTR__1__leave: + ret +___DATA__FUNCPTR__2: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 0A2DAh + push hl + ld hl, 00F49h + push hl + ld h, 082h + push hl + call __MULF + jp ___DATA__FUNCPTR__2__leave +___DATA__FUNCPTR__2__leave: + ret +__DATA__0: + DEFB 3 + DEFB 10 + DEFB 89h + DEFW ___DATA__FUNCPTR__0 + DEFB 89h + DEFW ___DATA__FUNCPTR__1 + DEFB 89h + DEFW ___DATA__FUNCPTR__2 +__DATA__END: + DEFB 00h +#line 1 "mulf.asm" + +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 2 "mulf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + + jp __FPSTACK_POP + +#line 113 "read5.bas" +#line 1 "pow.asm" + + + + ; ------------------------------------------------------------- + ; 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 + ; +; Operands comes swapped: + ; 1 st parameter is the BASE (A ED CB) + ; 2 nd parameter (Top of the stack) is Exponent + ; ------------------------------------------------------------- + +__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 + +#line 114 "read5.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 115 "read5.bas" +#line 1 "printf.asm" + +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 2 "printf.asm" + + + +__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 + +#line 116 "read5.bas" +#line 1 "read_restore.asm" + + ;; 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 + + +#line 1 "loadstr.asm" + +#line 1 "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 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 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. + + + + + + ; --------------------------------------------------------------------- + ; 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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" + + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 117 "read5.bas" +#line 1 "sin.asm" + + + +SIN: ; Computes SIN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 1Fh + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 118 "read5.bas" +#line 1 "storef.asm" + +__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 + +#line 119 "read5.bas" +#line 1 "tan.asm" + + + +TAN: ; Computes TAN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 21h ; TAN + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 120 "read5.bas" + +ZXBASIC_USER_DATA: +_v: + DEFB 81h + DEFB 40h + DEFB 00h + DEFB 00h + DEFB 00h +_c: + DEFB 00, 00, 00, 00, 00 +_d: + DEFB 00, 00, 00, 00, 00 +_i: + DEFB 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/read5.bas b/tests/functional/read5.bas new file mode 100644 index 000000000..c6e92284b --- /dev/null +++ b/tests/functional/read5.bas @@ -0,0 +1,18 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1.5 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, PI * v + + +DIM c, d as Float +FOR i = 1 TO 4: +READ c, d +PRINT c, d +NEXT i + + + + diff --git a/tests/functional/read6.bas b/tests/functional/read6.bas new file mode 100644 index 000000000..5d260da7a --- /dev/null +++ b/tests/functional/read6.bas @@ -0,0 +1,18 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1.5 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, PI * v + + +DIM c, d as Float +FOR i = 1 TO 4: +READ c * d +PRINT c +NEXT i + + + + diff --git a/tests/functional/read7.bas b/tests/functional/read7.bas new file mode 100644 index 000000000..7dcfc1819 --- /dev/null +++ b/tests/functional/read7.bas @@ -0,0 +1,17 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1.5 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, PI * v + + +DIM c(5) as Float +FOR i = 1 TO 4: +READ c +NEXT i + + + + diff --git a/tests/functional/read8.asm b/tests/functional/read8.asm new file mode 100644 index 000000000..ed27a823d --- /dev/null +++ b/tests/functional/read8.asm @@ -0,0 +1,2811 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld hl, __DATA__0 + call __RESTORE + ld a, 1 + ld (_i), a + jp __LABEL0 +__LABEL3: + ld a, 9 + call __READ + ld hl, _c + 8 + call __STOREF + ld a, (_c + 8) + ld de, (_c + 8 + 1) + ld bc, (_c + 8 + 3) + call __PRINTF + call PRINT_EOL +__LABEL4: + ld hl, _i + inc (hl) +__LABEL0: + ld a, 4 + ld hl, (_i - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +___DATA__FUNCPTR__0: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 00000h + push hl + ld hl, 00048h + push hl + ld h, 085h + push hl + call __MULF + jp ___DATA__FUNCPTR__0__leave +___DATA__FUNCPTR__0__leave: + ret +___DATA__FUNCPTR__1: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call SIN + push bc + push de + push af + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call TAN + push bc + push de + push af + ld a, 082h + ld de, 00000h + ld bc, 00000h + call __POW + call __MULF + jp ___DATA__FUNCPTR__1__leave +___DATA__FUNCPTR__1__leave: + ret +___DATA__FUNCPTR__2: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 0A2DAh + push hl + ld hl, 00F49h + push hl + ld h, 082h + push hl + call __MULF + jp ___DATA__FUNCPTR__2__leave +___DATA__FUNCPTR__2__leave: + ret +__DATA__0: + DEFB 3 + DEFB 10 + DEFB 89h + DEFW ___DATA__FUNCPTR__0 + DEFB 89h + DEFW ___DATA__FUNCPTR__1 + DEFB 89h + DEFW ___DATA__FUNCPTR__2 +__DATA__END: + DEFB 00h +#line 1 "mulf.asm" + +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 2 "mulf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + + jp __FPSTACK_POP + +#line 104 "read8.bas" +#line 1 "pow.asm" + + + + ; ------------------------------------------------------------- + ; 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 + ; +; Operands comes swapped: + ; 1 st parameter is the BASE (A ED CB) + ; 2 nd parameter (Top of the stack) is Exponent + ; ------------------------------------------------------------- + +__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 + +#line 105 "read8.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 106 "read8.bas" +#line 1 "printf.asm" + +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 2 "printf.asm" + + + +__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 + +#line 107 "read8.bas" +#line 1 "read_restore.asm" + + ;; 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 + + +#line 1 "loadstr.asm" + +#line 1 "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 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 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. + + + + + + ; --------------------------------------------------------------------- + ; 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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" + + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 108 "read8.bas" +#line 1 "sin.asm" + + + +SIN: ; Computes SIN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 1Fh + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 109 "read8.bas" +#line 1 "storef.asm" + +__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 + +#line 110 "read8.bas" +#line 1 "tan.asm" + + + +TAN: ; Computes TAN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 21h ; TAN + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 111 "read8.bas" + +ZXBASIC_USER_DATA: +_v: + DEFB 81h + DEFB 40h + DEFB 00h + DEFB 00h + DEFB 00h +_i: + DEFB 00 +_c: + DEFW 0000h + DEFB 05h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/read8.bas b/tests/functional/read8.bas new file mode 100644 index 000000000..c7340e4aa --- /dev/null +++ b/tests/functional/read8.bas @@ -0,0 +1,18 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1.5 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, PI * v + + +DIM c(3) as Float +FOR i = 1 TO 4: +READ c(1) +PRINT c(1) +NEXT i + + + + diff --git a/tests/functional/read9.asm b/tests/functional/read9.asm new file mode 100644 index 000000000..545e6a697 --- /dev/null +++ b/tests/functional/read9.asm @@ -0,0 +1,3027 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld hl, __DATA__0 + call __RESTORE + xor a + ld (_i), a + jp __LABEL0 +__LABEL3: + ld a, 9 + call __READ + push bc + push de + push af + ld a, (_i) + ld l, a + ld h, 0 + push hl + ld hl, _c + call __ARRAY + pop af + pop de + pop bc + call __STOREF + ld a, (_i) + ld l, a + ld h, 0 + push hl + ld hl, _c + call __ARRAY + call __LOADF + call __PRINTF + call PRINT_EOL +__LABEL4: + ld hl, _i + inc (hl) +__LABEL0: + ld a, 3 + ld hl, (_i - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +___DATA__FUNCPTR__0: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 00000h + push hl + ld hl, 00048h + push hl + ld h, 085h + push hl + call __MULF + jp ___DATA__FUNCPTR__0__leave +___DATA__FUNCPTR__0__leave: + ret +___DATA__FUNCPTR__1: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call SIN + push bc + push de + push af + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + call TAN + push bc + push de + push af + ld a, 082h + ld de, 00000h + ld bc, 00000h + call __POW + call __MULF + jp ___DATA__FUNCPTR__1__leave +___DATA__FUNCPTR__1__leave: + ret +___DATA__FUNCPTR__2: + ld a, (_v) + ld de, (_v + 1) + ld bc, (_v + 3) + ld hl, 0A2DAh + push hl + ld hl, 00F49h + push hl + ld h, 082h + push hl + call __MULF + jp ___DATA__FUNCPTR__2__leave +___DATA__FUNCPTR__2__leave: + ret +__DATA__0: + DEFB 3 + DEFB 10 + DEFB 89h + DEFW ___DATA__FUNCPTR__0 + DEFB 89h + DEFW ___DATA__FUNCPTR__1 + DEFB 89h + DEFW ___DATA__FUNCPTR__2 +__DATA__END: + DEFB 00h +#line 1 "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 "mul16.asm" + +__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: ; __FASTCALL ENTRY: HL = 1st operand, DE = 2nd Operand + ;; ld c, h + ;; ld a, l ; C,A => 1st Operand + ;; + ;; ld hl, 0 ; Accumulator + ;; ld b, 16 + ;; +;;__MUL16LOOP: + ;; sra c ; C,A >> 1 (Arithmetic) + ;; rra + ;; + ;; jr nc, __MUL16NOADD + ;; add hl, de + ;; +;;__MUL16NOADD: + ;; sla e + ;; rl d + ;; + ;; djnz __MUL16LOOP + +__MUL16_FAST: + ld b, 16 + ld a, d + ld c, e + ex de, hl + 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 + +#line 20 "array.asm" + +#line 24 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; BC = Offset "accumulator" + +LOOP: +#line 49 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack + +#line 59 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + + add hl, bc ; 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 + + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + + ;call __MUL16_FAST ; HL *= DE + call __FNMUL + jp LOOP + +ARRAY_END: + ld e, (hl) + inc hl + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 92 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + pop bc + ld b, c +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + + ;; Even faster + ;pop bc + + ;ld d, h + ;ld e, l + + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, hl + ;dec c + ;dec c + ;jp z, __ARRAY_FIN + + ;add hl, de + ;__ARRAY_FIN: +#line 123 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/array.asm" + + pop de + add hl, de ; Adds element start + +RET_ADDRESS: + ld de, 0 + push de + ret ; HL = (Start of Elements + Offset) + + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 + +__FNMUL: + xor a + or d + jp nz, __MUL16_FAST + + or e + ex de, hl + ret z + + cp 33 + jp nc, __MUL16_FAST + + ld b, l + ld l, h ; HL = 0 + +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + + ENDP + +#line 119 "read9.bas" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 120 "read9.bas" +#line 1 "mulf.asm" + +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 2 "mulf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + + jp __FPSTACK_POP + +#line 121 "read9.bas" +#line 1 "pow.asm" + + + + ; ------------------------------------------------------------- + ; 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 + ; +; Operands comes swapped: + ; 1 st parameter is the BASE (A ED CB) + ; 2 nd parameter (Top of the stack) is Exponent + ; ------------------------------------------------------------- + +__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 + +#line 122 "read9.bas" +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 123 "read9.bas" +#line 1 "printf.asm" + +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 2 "printf.asm" + + + +__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 + +#line 124 "read9.bas" +#line 1 "read_restore.asm" + + ;; 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 + + +#line 1 "loadstr.asm" + +#line 1 "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 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 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. + + + + + + ; --------------------------------------------------------------------- + ; 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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" + +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" + + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 125 "read9.bas" +#line 1 "sin.asm" + + + +SIN: ; Computes SIN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 1Fh + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 126 "read9.bas" +#line 1 "storef.asm" + +__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 + +#line 127 "read9.bas" +#line 1 "tan.asm" + + + +TAN: ; Computes TAN using ROM FP-CALC + call __FPSTACK_PUSH + + rst 28h ; ROM CALC + defb 21h ; TAN + defb 38h ; END CALC + + jp __FPSTACK_POP + +#line 128 "read9.bas" + +ZXBASIC_USER_DATA: +_v: + DEFB 81h + DEFB 40h + DEFB 00h + DEFB 00h + DEFB 00h +_i: + DEFB 00 +_c: + DEFW 0000h + DEFB 05h + 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 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/read9.bas b/tests/functional/read9.bas new file mode 100644 index 000000000..f95a7064f --- /dev/null +++ b/tests/functional/read9.bas @@ -0,0 +1,20 @@ +REM Error x is an array, not an scalar + +DIM v as Float = 1.5 + +RESTORE + +DATA 10, 25 * v, SIN(v) * tan(v)^2, PI * v + +DIM i as UByte + + +DIM c(3) as Float +FOR i = 0 TO 3: +READ c(i) +PRINT c(i) +NEXT i + + + + diff --git a/tests/functional/readbug.asm b/tests/functional/readbug.asm new file mode 100644 index 000000000..853a4b5e7 --- /dev/null +++ b/tests/functional/readbug.asm @@ -0,0 +1,1351 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld a, 3 + call __READ + ld (_r1), a + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__DATA__0: +__DATA__END: + DEFB 00h +#line 1 "read_restore.asm" + + ;; 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 + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 23 "read_restore.asm" +#line 1 "loadstr.asm" + +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "ftou32reg.asm" + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 29 "read_restore.asm" + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 24 "readbug.bas" + +ZXBASIC_USER_DATA: +_r1: + DEFB 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/readbug.bas b/tests/functional/readbug.bas new file mode 100644 index 000000000..ca257f122 --- /dev/null +++ b/tests/functional/readbug.bas @@ -0,0 +1,2 @@ +DIM r1 as UByte +READ r1 diff --git a/tests/functional/readokdown.asm b/tests/functional/readokdown.asm new file mode 100644 index 000000000..87d8a4875 --- /dev/null +++ b/tests/functional/readokdown.asm @@ -0,0 +1,3295 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld hl, __DATA__0 + call __RESTORE + ld a, 2 + call __READ + ld (_i8), a + call __PRINTI8 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 3 + call __READ + ld (_u8), a + call __PRINTU8 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 4 + call __READ + ld (_i16), hl + call __PRINTI16 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 5 + call __READ + ld (_u16), hl + call __PRINTU16 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 6 + call __READ + ld (_i32), hl + ld (_i32 + 2), de + ld hl, (_i32) + ld de, (_i32 + 2) + call __PRINTI32 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 7 + call __READ + ld (_u32), hl + ld (_u32 + 2), de + ld hl, (_u32) + ld de, (_u32 + 2) + call __PRINTU32 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 8 + call __READ + ld (_f16), hl + ld (_f16 + 2), de + ld hl, (_f16) + ld de, (_f16 + 2) + call __PRINTF16 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 9 + call __READ + ld hl, _flt + call __STOREF + ld a, (_flt) + ld de, (_flt + 1) + ld bc, (_flt + 3) + call __PRINTF + call PRINT_EOL + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__DATA__0: + DEFB 9 + DEFB 090h + DEFW 0E880h, 04627h +__DATA__END: + DEFB 00h +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 93 "readokdown.bas" +#line 1 "printf.asm" + +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 2 "printf.asm" +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 3 "printf.asm" + + +__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 + +#line 94 "readokdown.bas" +#line 1 "printf16.asm" + +#line 1 "printnum.asm" + + + + +__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 mus preserve registers + ld a, '-' + jp __PRINT_DIGIT + + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + + +#line 2 "printf16.asm" +#line 1 "printi16.asm" + + +#line 1 "div16.asm" + + ; 16 bit division and modulo functions + ; for both signed and unsigned values + +#line 1 "neg16.asm" + + ; Negates HL value (16 bit) +__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 + +#line 5 "div16.asm" + +__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 + +#line 3 "printi16.asm" + + + +__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 + +#line 3 "printf16.asm" +#line 1 "neg32.asm" + +__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 + +#line 4 "printf16.asm" + +__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 + +#line 95 "readokdown.bas" + +#line 1 "printi32.asm" + + + +#line 1 "div32.asm" + + + + ; --------------------------------------------------------- +__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 + +#line 4 "printi32.asm" + + + +__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 + +#line 97 "readokdown.bas" +#line 1 "printi8.asm" + + +#line 1 "div8.asm" + + ; -------------------------------- +__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 + +#line 3 "printi8.asm" + +__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 + +#line 98 "readokdown.bas" +#line 1 "printu16.asm" + + + +#line 99 "readokdown.bas" +#line 1 "printu32.asm" + + + +#line 100 "readokdown.bas" +#line 1 "printu8.asm" + + + +#line 101 "readokdown.bas" +#line 1 "read_restore.asm" + + ;; 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 + + +#line 1 "loadstr.asm" + +#line 1 "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 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 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. + + + + + + ; --------------------------------------------------------------------- + ; 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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + + + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" + + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 102 "readokdown.bas" +#line 1 "storef.asm" + +__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 + +#line 103 "readokdown.bas" + +ZXBASIC_USER_DATA: +_i8: + DEFB 00 +_u8: + DEFB 00 +_i16: + DEFB 00, 00 +_u16: + DEFB 00, 00 +_i32: + DEFB 00, 00, 00, 00 +_u32: + DEFB 00, 00, 00, 00 +_f16: + DEFB 00, 00, 00, 00 +_flt: + DEFB 00, 00, 00, 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/readokdown.bas b/tests/functional/readokdown.bas new file mode 100644 index 000000000..66f6b15e7 --- /dev/null +++ b/tests/functional/readokdown.bas @@ -0,0 +1,43 @@ +REM Type coerding (reduction) in read DATA + +DIM i8 as Byte +DIM u8 as UByte +DIM i16 as Integer +DIM u16 as UInteger +DIM i32 as Long +DIM u32 as ULong +DIM f16 as Fixed +DIM flt as Float + +RESTORE +READ i8 +PRINT i8 +RESTORE +READ u8 +PRINT u8 + +RESTORE +READ i16 +PRINT i16 +RESTORE +READ u16 +PRINT u16 + +RESTORE +READ i32 +PRINT i32 +RESTORE +READ u32 +PRINT u32 + +RESTORE +READ f16 +PRINT f16 + +RESTORE +READ flt +PRINT flt + +DATA -33000.153423423 + + diff --git a/tests/functional/readokup.asm b/tests/functional/readokup.asm new file mode 100644 index 000000000..1f11b134f --- /dev/null +++ b/tests/functional/readokup.asm @@ -0,0 +1,3294 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + ld hl, __DATA__0 + call __RESTORE + ld a, 2 + call __READ + ld (_i8), a + call __PRINTI8 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 3 + call __READ + ld (_u8), a + call __PRINTU8 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 4 + call __READ + ld (_i16), hl + call __PRINTI16 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 5 + call __READ + ld (_u16), hl + call __PRINTU16 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 6 + call __READ + ld (_i32), hl + ld (_i32 + 2), de + ld hl, (_i32) + ld de, (_i32 + 2) + call __PRINTI32 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 7 + call __READ + ld (_u32), hl + ld (_u32 + 2), de + ld hl, (_u32) + ld de, (_u32 + 2) + call __PRINTU32 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 8 + call __READ + ld (_f16), hl + ld (_f16 + 2), de + ld hl, (_f16) + ld de, (_f16 + 2) + call __PRINTF16 + call PRINT_EOL + ld hl, __DATA__0 + call __RESTORE + ld a, 9 + call __READ + ld hl, _flt + call __STOREF + ld a, (_flt) + ld de, (_flt + 1) + ld bc, (_flt + 3) + call __PRINTF + call PRINT_EOL + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__DATA__0: + DEFB 2 + DEFB -1 +__DATA__END: + DEFB 00h +#line 1 "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 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 6 "print.asm" +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + + + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 7 "print.asm" +#line 1 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 92 "readokup.bas" +#line 1 "printf.asm" + +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 2 "printf.asm" +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 3 "printf.asm" + + +__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 + +#line 93 "readokup.bas" +#line 1 "printf16.asm" + +#line 1 "printnum.asm" + + + + +__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 mus preserve registers + ld a, '-' + jp __PRINT_DIGIT + + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + + +#line 2 "printf16.asm" +#line 1 "printi16.asm" + + +#line 1 "div16.asm" + + ; 16 bit division and modulo functions + ; for both signed and unsigned values + +#line 1 "neg16.asm" + + ; Negates HL value (16 bit) +__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 + +#line 5 "div16.asm" + +__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 + +#line 3 "printi16.asm" + + + +__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 + +#line 3 "printf16.asm" +#line 1 "neg32.asm" + +__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 + +#line 4 "printf16.asm" + +__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 + +#line 94 "readokup.bas" + +#line 1 "printi32.asm" + + + +#line 1 "div32.asm" + + + + ; --------------------------------------------------------- +__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 + +#line 4 "printi32.asm" + + + +__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 + +#line 96 "readokup.bas" +#line 1 "printi8.asm" + + +#line 1 "div8.asm" + + ; -------------------------------- +__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 + +#line 3 "printi8.asm" + +__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 + +#line 97 "readokup.bas" +#line 1 "printu16.asm" + + + +#line 98 "readokup.bas" +#line 1 "printu32.asm" + + + +#line 99 "readokup.bas" +#line 1 "printu8.asm" + + + +#line 100 "readokup.bas" +#line 1 "read_restore.asm" + + ;; 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 + + +#line 1 "loadstr.asm" + +#line 1 "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 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 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. + + + + + + ; --------------------------------------------------------------------- + ; 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) + ; --------------------------------------------------------------------- + +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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 + + +#line 2 "loadstr.asm" + + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + +__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 +#line 24 "read_restore.asm" +#line 1 "iload32.asm" + + ; __FASTCALL__ routine which + ; loads a 32 bits integer into DE,HL + ; stored at position pointed by POINTER HL + ; DE,HL <-- (HL) + +__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 + +#line 25 "read_restore.asm" +#line 1 "iloadf.asm" + + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + +__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 + +#line 26 "read_restore.asm" +#line 1 "ftof16reg.asm" + +#line 1 "ftou32reg.asm" + + + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 2 "ftof16reg.asm" + +__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 + +#line 27 "read_restore.asm" +#line 1 "f16tofreg.asm" + + +#line 1 "u32tofreg.asm" + + +__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 + +#line 3 "f16tofreg.asm" + +__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 + +#line 28 "read_restore.asm" + + + + + + + + + + + + + + ;; Updates restore point to the given HL mem. address +__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__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 1 ; 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 1 + 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 2 + 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 4 + 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 6 + 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 7 + ret z ;; From i32 to u32 + ret c ;; From u16 to i32 + cp 9 + jp z, __I32TOFREG +_from_u32: + cp 9 + jp z, __U32TOFREG + ex de, hl + ld hl, 0 + cp 8 + 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 + cp 8 ;; 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 8 ;; + 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 4 + 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__0 + ENDP + + + + + + + + + + +#line 101 "readokup.bas" +#line 1 "storef.asm" + +__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 + +#line 102 "readokup.bas" + +ZXBASIC_USER_DATA: +_i8: + DEFB 00 +_u8: + DEFB 00 +_i16: + DEFB 00, 00 +_u16: + DEFB 00, 00 +_i32: + DEFB 00, 00, 00, 00 +_u32: + DEFB 00, 00, 00, 00 +_f16: + DEFB 00, 00, 00, 00 +_flt: + DEFB 00, 00, 00, 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/readokup.bas b/tests/functional/readokup.bas new file mode 100644 index 000000000..14a0a95d9 --- /dev/null +++ b/tests/functional/readokup.bas @@ -0,0 +1,46 @@ +REM Type promoting in read DATA + +DIM i8 as Byte +DIM u8 as UByte +DIM i16 as Integer +DIM u16 as UInteger +DIM i32 as Long +DIM u32 as ULong +DIM f16 as Fixed +DIM flt as Float + +RESTORE +READ i8 +PRINT i8 + +RESTORE +READ u8 +PRINT u8 + +RESTORE +READ i16 +PRINT i16 + +RESTORE +READ u16 +PRINT u16 + +RESTORE +READ i32 +PRINT i32 + +RESTORE +READ u32 +PRINT u32 + +RESTORE +READ f16 +PRINT f16 + +RESTORE +READ flt +PRINT flt + +DATA -1 + + diff --git a/tests/functional/recur0.asm b/tests/functional/recur0.asm index 42b8e85d7..53d4bd283 100644 --- a/tests/functional/recur0.asm +++ b/tests/functional/recur0.asm @@ -35,7 +35,7 @@ _Test__leave: ld sp, ix pop ix ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/refconstparam.asm b/tests/functional/refconstparam.asm index 83d9c971c..b6674b390 100644 --- a/tests/functional/refconstparam.asm +++ b/tests/functional/refconstparam.asm @@ -44,7 +44,7 @@ _x__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/refconstparam2.asm b/tests/functional/refconstparam2.asm index 83d9c971c..b6674b390 100644 --- a/tests/functional/refconstparam2.asm +++ b/tests/functional/refconstparam2.asm @@ -44,7 +44,7 @@ _x__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/refconstparam4.asm b/tests/functional/refconstparam4.asm index d10eea80c..f8f398b90 100644 --- a/tests/functional/refconstparam4.asm +++ b/tests/functional/refconstparam4.asm @@ -43,7 +43,7 @@ _x__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: _b: DEFB 00, 00 diff --git a/tests/functional/refconstparam5.asm b/tests/functional/refconstparam5.asm index 4185e5b8e..503001f25 100644 --- a/tests/functional/refconstparam5.asm +++ b/tests/functional/refconstparam5.asm @@ -42,7 +42,7 @@ _x__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/refconstparam6.asm b/tests/functional/refconstparam6.asm index 4185e5b8e..503001f25 100644 --- a/tests/functional/refconstparam6.asm +++ b/tests/functional/refconstparam6.asm @@ -42,7 +42,7 @@ _x__leave: ex (sp), hl exx ret - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/rman.bas b/tests/functional/rman.bas old mode 100755 new mode 100644 diff --git a/tests/functional/save01.asm b/tests/functional/save01.asm index d4dab681d..089346e10 100644 --- a/tests/functional/save01.asm +++ b/tests/functional/save01.asm @@ -48,10 +48,12 @@ __LABEL0: DEFB 74h DEFB 73h #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -59,25 +61,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -86,50 +88,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -137,12 +140,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -150,7 +154,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -158,9 +162,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -168,25 +173,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -195,39 +200,39 @@ __STOP: ; | (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 + ; 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 @@ -235,57 +240,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -297,39 +302,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -337,15 +342,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -370,14 +375,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -385,25 +390,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -412,30 +417,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -443,74 +448,75 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 36 "save01.bas" #line 1 "save.asm" + ; 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. - - - + + + SAVE_CODE: - + PROC - + LOCAL MEMBOT LOCAL SAVE_CONT LOCAL ROM_SAVE LOCAL __ERR_EMPTY LOCAL SAVE_STOP - - ROM_SAVE EQU 0970h + + ROM_SAVE EQU 0970h MEMBOT EQU 23698 ; Use the CALC mem to store header - + pop hl ; Return address pop bc ; data length in bytes pop de ; address start ex (sp), hl ; CALLE => now hl = String - + ; 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 ret z ; 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 - + 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 @@ -518,27 +524,27 @@ __ERR_EMPTY: 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 l, (ix + 13) - ld h, (ix + 14) ; Restores start of bytes - + ld h, (ix + 14) ; Restores start of bytes + call ROM_SAVE ; Recovers ECHO_E since ROM SAVE changes it ld hl, 1821h ld (23682), hl pop ix ret - + SAVE_STOP: pop ix jp __STOP - + ENDP #line 37 "save01.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/save02.asm b/tests/functional/save02.asm index c36291968..4dd1c6d79 100644 --- a/tests/functional/save02.asm +++ b/tests/functional/save02.asm @@ -45,10 +45,12 @@ __LABEL0: DEFB 74h DEFB 31h #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -56,25 +58,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -83,50 +85,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -134,12 +137,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -147,7 +151,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -155,9 +159,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -165,25 +170,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -192,39 +197,39 @@ __STOP: ; | (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 + ; 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 @@ -232,57 +237,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -294,39 +299,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -334,15 +339,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -367,14 +372,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -382,25 +387,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -409,30 +414,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -440,74 +445,75 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 33 "save02.bas" #line 1 "save.asm" + ; 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. - - - + + + SAVE_CODE: - + PROC - + LOCAL MEMBOT LOCAL SAVE_CONT LOCAL ROM_SAVE LOCAL __ERR_EMPTY LOCAL SAVE_STOP - - ROM_SAVE EQU 0970h + + ROM_SAVE EQU 0970h MEMBOT EQU 23698 ; Use the CALC mem to store header - + pop hl ; Return address pop bc ; data length in bytes pop de ; address start ex (sp), hl ; CALLE => now hl = String - + ; 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 ret z ; 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 - + 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 @@ -515,27 +521,27 @@ __ERR_EMPTY: 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 l, (ix + 13) - ld h, (ix + 14) ; Restores start of bytes - + ld h, (ix + 14) ; Restores start of bytes + call ROM_SAVE ; Recovers ECHO_E since ROM SAVE changes it ld hl, 1821h ld (23682), hl pop ix ret - + SAVE_STOP: pop ix jp __STOP - + ENDP #line 34 "save02.bas" - + ZXBASIC_USER_DATA: _variableToSave: DEFB 00, 00 diff --git a/tests/functional/save03.asm b/tests/functional/save03.asm index c2f49c239..302a785b7 100644 --- a/tests/functional/save03.asm +++ b/tests/functional/save03.asm @@ -44,10 +44,12 @@ __LABEL0: DEFB 73h DEFB 74h #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -55,25 +57,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -82,50 +84,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -133,12 +136,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -146,7 +150,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -154,9 +158,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -164,25 +169,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -191,39 +196,39 @@ __STOP: ; | (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 + ; 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 @@ -231,57 +236,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -293,39 +298,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -333,15 +338,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -366,14 +371,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -381,25 +386,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -408,30 +413,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -439,74 +444,75 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 32 "save03.bas" #line 1 "save.asm" + ; 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. - - - + + + SAVE_CODE: - + PROC - + LOCAL MEMBOT LOCAL SAVE_CONT LOCAL ROM_SAVE LOCAL __ERR_EMPTY LOCAL SAVE_STOP - - ROM_SAVE EQU 0970h + + ROM_SAVE EQU 0970h MEMBOT EQU 23698 ; Use the CALC mem to store header - + pop hl ; Return address pop bc ; data length in bytes pop de ; address start ex (sp), hl ; CALLE => now hl = String - + ; 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 ret z ; 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 - + 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 @@ -514,27 +520,27 @@ __ERR_EMPTY: 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 l, (ix + 13) - ld h, (ix + 14) ; Restores start of bytes - + ld h, (ix + 14) ; Restores start of bytes + call ROM_SAVE ; Recovers ECHO_E since ROM SAVE changes it ld hl, 1821h ld (23682), hl pop ix ret - + SAVE_STOP: pop ix jp __STOP - + ENDP #line 33 "save03.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/sgnf.asm b/tests/functional/sgnf.asm index 070e8f2f5..1cba35e7a 100644 --- a/tests/functional/sgnf.asm +++ b/tests/functional/sgnf.asm @@ -32,12 +32,14 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sgnf.asm" + #line 1 "sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT - + PROC LOCAL __ENDSGN - + __SGNF: or b or c @@ -46,7 +48,7 @@ __SGNF: ret z ld a, e jr __ENDSGN - + __SGNF16: __SGNI32: ld a, h @@ -54,29 +56,29 @@ __SGNI32: 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 - + #line 2 "sgnf.asm" - + #line 23 "sgnf.bas" - + ZXBASIC_USER_DATA: _y: DEFB 81h diff --git a/tests/functional/sgnf16.asm b/tests/functional/sgnf16.asm index c50045f21..3febd98f9 100644 --- a/tests/functional/sgnf16.asm +++ b/tests/functional/sgnf16.asm @@ -31,12 +31,14 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sgnf16.asm" + #line 1 "sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT - + PROC LOCAL __ENDSGN - + __SGNF: or b or c @@ -45,7 +47,7 @@ __SGNF: ret z ld a, e jr __ENDSGN - + __SGNF16: __SGNI32: ld a, h @@ -53,29 +55,29 @@ __SGNI32: 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 - + #line 2 "sgnf16.asm" - + #line 22 "sgnf16.bas" - + ZXBASIC_USER_DATA: _y: DEFB 00h diff --git a/tests/functional/sgni16.asm b/tests/functional/sgni16.asm index d5702bd4d..270151ad6 100644 --- a/tests/functional/sgni16.asm +++ b/tests/functional/sgni16.asm @@ -30,12 +30,14 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sgni16.asm" + #line 1 "sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT - + PROC LOCAL __ENDSGN - + __SGNF: or b or c @@ -44,7 +46,7 @@ __SGNF: ret z ld a, e jr __ENDSGN - + __SGNF16: __SGNI32: ld a, h @@ -52,29 +54,29 @@ __SGNI32: 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 - + #line 2 "sgni16.asm" - + #line 21 "sgni16.bas" - + ZXBASIC_USER_DATA: _y: DEFB 01h diff --git a/tests/functional/sgni32.asm b/tests/functional/sgni32.asm index 9db7778e8..de9aa29ef 100644 --- a/tests/functional/sgni32.asm +++ b/tests/functional/sgni32.asm @@ -31,12 +31,14 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sgni32.asm" + #line 1 "sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT - + PROC LOCAL __ENDSGN - + __SGNF: or b or c @@ -45,7 +47,7 @@ __SGNF: ret z ld a, e jr __ENDSGN - + __SGNF16: __SGNI32: ld a, h @@ -53,29 +55,29 @@ __SGNI32: 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 - + #line 2 "sgni32.asm" - + #line 22 "sgni32.bas" - + ZXBASIC_USER_DATA: _y: DEFB 01h diff --git a/tests/functional/sgni8.asm b/tests/functional/sgni8.asm index 1c0272690..b856942a1 100644 --- a/tests/functional/sgni8.asm +++ b/tests/functional/sgni8.asm @@ -30,8 +30,9 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sgni8.asm" + ; Returns SGN (SIGN) for 8 bits signed integer - + __SGNI8: or a ret z @@ -39,9 +40,9 @@ __SGNI8: ret p neg ret - + #line 21 "sgni8.bas" - + ZXBASIC_USER_DATA: _y: DEFB 01h diff --git a/tests/functional/sgnu16.asm b/tests/functional/sgnu16.asm index 8189cd945..8ed3e7cb1 100644 --- a/tests/functional/sgnu16.asm +++ b/tests/functional/sgnu16.asm @@ -30,17 +30,18 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sgnu16.asm" + ; Returns SGN (SIGN) for 16 bits unsigned integer - + __SGNU16: ld a, h or l ret z ld a, 1 ret - + #line 21 "sgnu16.bas" - + ZXBASIC_USER_DATA: _y: DEFB 01h diff --git a/tests/functional/sgnu32.asm b/tests/functional/sgnu32.asm index 1e9314731..edb98b2c3 100644 --- a/tests/functional/sgnu32.asm +++ b/tests/functional/sgnu32.asm @@ -31,20 +31,21 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sgnu32.asm" + ; Returns SGN (SIGN) for 32 bits unsigned integer - + __SGNU32: ld a, h or l or d or e ret z - + ld a, 1 ret - + #line 22 "sgnu32.bas" - + ZXBASIC_USER_DATA: _y: DEFB 01h diff --git a/tests/functional/sgnu8.asm b/tests/functional/sgnu8.asm index a0c5c4f9a..069574253 100644 --- a/tests/functional/sgnu8.asm +++ b/tests/functional/sgnu8.asm @@ -30,16 +30,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sgnu8.asm" + ; Returns SGN (SIGN) for 8 bits unsigned integera - + __SGNU8: or a ret z ld a, 1 ret - + #line 21 "sgnu8.bas" - + ZXBASIC_USER_DATA: _y: DEFB 01h diff --git a/tests/functional/shl8.asm b/tests/functional/shl8.asm index ffec09fab..603bdf7c6 100644 --- a/tests/functional/shl8.asm +++ b/tests/functional/shl8.asm @@ -42,7 +42,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/shri8.asm b/tests/functional/shri8.asm index b0b2ac061..b7e6b9411 100644 --- a/tests/functional/shri8.asm +++ b/tests/functional/shri8.asm @@ -42,7 +42,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/shru8.asm b/tests/functional/shru8.asm index 5c9094095..104832de8 100644 --- a/tests/functional/shru8.asm +++ b/tests/functional/shru8.asm @@ -42,7 +42,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/sigilfunc.asm b/tests/functional/sigilfunc.asm index e0cb3f5fa..debffd0e0 100644 --- a/tests/functional/sigilfunc.asm +++ b/tests/functional/sigilfunc.asm @@ -46,10 +46,12 @@ _test__leave: __LABEL0: DEFW 0000h #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -57,25 +59,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -84,50 +86,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -135,12 +138,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -148,7 +152,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -156,9 +160,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -166,25 +171,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -193,39 +198,39 @@ __STOP: ; | (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 + ; 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 @@ -233,57 +238,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -295,39 +300,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -335,15 +340,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -368,14 +373,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -383,25 +388,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -410,30 +415,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -441,17 +446,19 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 34 "sigilfunc.bas" #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -459,25 +466,25 @@ __LOADSTR: ; __FASTCALL__ entry ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -486,38 +493,38 @@ __LOADSTR: ; __FASTCALL__ entry ; | (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 + ; 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 @@ -526,57 +533,57 @@ __LOADSTR: ; __FASTCALL__ entry ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -585,47 +592,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -635,43 +642,43 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 35 "sigilfunc.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/simple.asm b/tests/functional/simple.asm index e49b98724..a7c4850a2 100644 --- a/tests/functional/simple.asm +++ b/tests/functional/simple.asm @@ -48,15 +48,17 @@ __LABEL0: DEFB 4Ch DEFB 44h #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -64,45 +66,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -115,41 +118,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -157,12 +162,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -170,50 +176,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -222,15 +229,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -238,26 +247,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -271,44 +280,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -319,27 +329,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -347,27 +358,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -376,81 +388,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -459,32 +473,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -494,7 +508,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -502,19 +516,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -524,7 +539,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -535,18 +550,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -556,7 +572,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -567,18 +583,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -588,7 +605,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -601,21 +618,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -624,79 +642,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -705,75 +723,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -783,7 +801,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -795,16 +813,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -818,49 +836,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -871,17 +889,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -894,27 +912,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -922,10 +940,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -942,80 +960,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1032,8 +1050,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1057,17 +1075,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1078,7 +1096,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1088,21 +1106,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1121,9 +1139,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1148,19 +1166,21 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 35 "simple.bas" #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1168,25 +1188,25 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1195,40 +1215,41 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1236,25 +1257,25 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1263,39 +1284,39 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ; | (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 + ; 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 @@ -1303,56 +1324,56 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1361,57 +1382,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1420,47 +1441,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1470,51 +1491,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1523,11 +1544,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 36 "simple.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/slice0.asm b/tests/functional/slice0.asm index 2c0f20f71..138bb5cdc 100644 --- a/tests/functional/slice0.asm +++ b/tests/functional/slice0.asm @@ -41,17 +41,19 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -59,25 +61,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -86,40 +88,41 @@ __CALL_BACK__: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -127,25 +130,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -154,39 +157,39 @@ __CALL_BACK__: ; | (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 + ; 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 @@ -194,56 +197,56 @@ __CALL_BACK__: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -252,57 +255,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -311,47 +314,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -361,81 +364,84 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 29 "slice0.bas" #line 1 "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 + + ; 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 + ; it in dynamic memory if needed). Returns pointer (HL) to resulting ; string. NULL (0) if no memory for padding. ; - + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 18 "strslice.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -443,25 +449,25 @@ __STRLEN: ; Direct FASTCALL entry ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -470,50 +476,51 @@ __STRLEN: ; Direct FASTCALL entry ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -521,12 +528,13 @@ __STRLEN: ; Direct FASTCALL entry ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -534,16 +542,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -555,39 +563,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -595,15 +603,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -628,14 +636,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -643,41 +651,41 @@ __MEM_SUBTRACT: 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 - - + + #line 19 "strslice.asm" - - + + __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) + 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 @@ -685,19 +693,19 @@ __STRSLICE_FAST: ; __FASTCALL__ Entry ld b, h ld c, l ; Copy to the end of str ccf ; Clears Carry flag for next subtraction - + __CONT: - ld h, b + 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 @@ -706,15 +714,15 @@ __CONT: 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 @@ -727,26 +735,26 @@ __CONT: 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 - + ret + + ENDP + #line 30 "slice0.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/slice2.asm b/tests/functional/slice2.asm index 06d5d5ff6..253cdd1e1 100644 --- a/tests/functional/slice2.asm +++ b/tests/functional/slice2.asm @@ -76,9 +76,7 @@ __LABEL4: add a, 2 ld (ix+7), a __LABEL5: - ld a, (ix-1) - inc a - ld (ix-1), a + inc (ix-1) __LABEL1: ld a, (ix-1) push af @@ -125,18 +123,20 @@ __LABEL0: DEFB 6Ch DEFB 64h #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -144,36 +144,36 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 9 "cls.asm" - + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -186,39 +186,40 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - -#line 113 "slice2.bas" + +#line 111 "slice2.bas" #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -226,12 +227,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -239,17 +241,18 @@ __ERROR: __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 -#line 114 "slice2.bas" +#line 112 "slice2.bas" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -257,25 +260,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -284,40 +287,41 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -325,25 +329,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -352,39 +356,39 @@ __STOP: ; | (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 + ; 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 @@ -392,56 +396,56 @@ __STOP: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -450,57 +454,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -509,47 +513,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -559,15 +563,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - -#line 115 "slice2.bas" + +#line 113 "slice2.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -575,25 +581,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -602,40 +608,40 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -647,39 +653,39 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -687,15 +693,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -720,14 +726,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -735,25 +741,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -762,151 +768,155 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 -#line 116 "slice2.bas" +#line 114 "slice2.bas" #line 1 "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 "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. - - - + + + __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 - + #line 9 "pstorestr2.asm" - + __PSTORE_STR2: push ix pop hl add hl, bc jp __STORE_STR2 - -#line 117 "slice2.bas" + +#line 115 "slice2.bas" #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - -#line 118 "slice2.bas" + + +#line 116 "slice2.bas" #line 1 "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 + + ; 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 + ; it in dynamic memory if needed). Returns pointer (HL) to resulting ; string. NULL (0) if no memory for padding. ; - - - - - + + + + + __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) + 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 @@ -914,19 +924,19 @@ __STRSLICE_FAST: ; __FASTCALL__ Entry ld b, h ld c, l ; Copy to the end of str ccf ; Clears Carry flag for next subtraction - + __CONT: - ld h, b + 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 @@ -935,15 +945,15 @@ __CONT: 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 @@ -956,26 +966,26 @@ __CONT: 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 - -#line 119 "slice2.bas" - + ret + + ENDP + +#line 117 "slice2.bas" + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/spfill.asm b/tests/functional/spfill.asm index 454b9fa72..652b5dada 100644 --- a/tests/functional/spfill.asm +++ b/tests/functional/spfill.asm @@ -65,7 +65,7 @@ _SPFill: call SPPFill_start pop ix ret -#line 1 "PixelUp.asm" +#line 1 "/src/zxb/trunk/library-asm/SP/PixelUp.asm" SP.PixelUp: ld a,h dec h @@ -86,8 +86,8 @@ _SPFill: ld h,a cp $40 ret -#line 31 "Fill.bas" -#line 1 "PixelDown.asm" +#line 31 "/src/zxb/trunk/library/SP/Fill.bas" +#line 1 "/src/zxb/trunk/library-asm/SP/PixelDown.asm" SP.PixelDown: inc h ld a,h @@ -109,8 +109,8 @@ _SPFill: cp $58 ccf ret -#line 32 "Fill.bas" -#line 1 "CharLeft.asm" +#line 32 "/src/zxb/trunk/library/SP/Fill.bas" +#line 1 "/src/zxb/trunk/library-asm/SP/CharLeft.asm" SP.CharLeft: ld a,l dec l @@ -121,8 +121,8 @@ _SPFill: ld h,a cp $40 ret -#line 33 "Fill.bas" -#line 1 "CharRight.asm" +#line 33 "/src/zxb/trunk/library/SP/Fill.bas" +#line 1 "/src/zxb/trunk/library-asm/SP/CharRight.asm" SP.CharRight: inc l ret nz @@ -132,8 +132,8 @@ _SPFill: cp $58 ccf ret -#line 34 "Fill.bas" -#line 1 "GetScrnAddr.asm" +#line 34 "/src/zxb/trunk/library/SP/Fill.bas" +#line 1 "/src/zxb/trunk/library-asm/SP/GetScrnAddr.asm" SPGetScrnAddr: and $07 or $40 @@ -165,7 +165,7 @@ norotate: or l ld e,a ret -#line 35 "Fill.bas" +#line 35 "/src/zxb/trunk/library/SP/Fill.bas" SPPFill_IXBuffer: DEFB 0,0 SPPFill_start: @@ -481,23 +481,25 @@ __LABEL0: DEFW 0001h DEFB 61h #line 1 "circle.asm" + ; Bresenham's like circle algorithm ; best known as Middle Point Circle drawing algorithm - + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -505,12 +507,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -518,7 +521,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -526,19 +529,22 @@ __STOP: ret #line 5 "circle.asm" #line 1 "plot.asm" + ; 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 - - + + #line 1 "in_screen.asm" + #line 1 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -546,74 +552,75 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 2 "in_screen.asm" - - + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 9 "plot.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -626,48 +633,48 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 10 "plot.asm" - + 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 - + ld c, h + 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 @@ -675,7 +682,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) res 6, h ; Starts from 0 ld bc, (SCREEN_ADDR) add hl, bc ; Now current offset - + ld b, a inc b ld a, 0FEh @@ -683,7 +690,7 @@ __PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) __PLOT_LOOP: rrca djnz __PLOT_LOOP - + ld b, a ld a, (P_FLAG) ld c, a @@ -691,18 +698,18 @@ __PLOT_LOOP: 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 - + ;; gets ATTR position with offset given in SCREEN_ADDR ld a, h rrca @@ -713,36 +720,36 @@ __PLOT_END: ld h, a ld de, (SCREEN_ADDR) add hl, de ;; Final screen addr - + LOCAL PO_ATTR_2 PO_ATTR_2 EQU 0BE4h ; Another entry to PO_ATTR jp PO_ATTR_2 ; This will update attr accordingly. Beware, uses IY - + __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 + PIXEL_ADDR EQU 22ACh COORDS EQU 5C7Dh ENDP #line 6 "circle.asm" - - + + ; Draws a circle at X, Y of radius R ; X, Y on the Stack, R in accumulator (Byte) - + PROC LOCAL __CIRCLE_ERROR LOCAL __CIRCLE_LOOP LOCAL __CIRCLE_NEXT - + __CIRCLE_ERROR: jp __OUT_OF_SCREEN_ERR ;; __CIRCLE_ERROR EQU __OUT_OF_SCREEN_ERR ;; __CIRCLE_ERROR: ;; ; Jumps here if out of screen ;; scf ; Always sets carry Flag - ;; + ;; ;; ld a, ERROR_OutOfScreen ;; ld (ERR_NR), a ;; ret @@ -752,95 +759,95 @@ CIRCLE: pop de ; D = Y ex (sp), hl ; __CALLEE__ convention ld e, h ; E = X - - - ld h, a ; H = R + + + ld h, a ; H = R 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 + 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 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 @@ -850,11 +857,11 @@ __CIRCLE_NEXT: ld h, a pop af ex af, af' - - push bc + + push bc exx pop hl ; H'L' = Y, X - + ld a, d add a, h ld b, a ; B = y0 + y @@ -862,7 +869,7 @@ __CIRCLE_NEXT: 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 @@ -870,7 +877,7 @@ __CIRCLE_NEXT: 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 @@ -878,7 +885,7 @@ __CIRCLE_NEXT: 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 @@ -886,76 +893,79 @@ __CIRCLE_NEXT: sub l ld c, a ; C = x0 - x call __CIRCLE_PLOT ; plot(x0 - x, y0 - y) - + ld a, d add a, l ld b, a ; B = y0 + x - ld a, e + 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 + 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 + 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 + ld a, e sub h ld c, a ; C = x0 + y call __CIRCLE_PLOT ; plot(x0 - y, y0 - x) - + exx jp __CIRCLE_LOOP - - - + + + __CIRCLE_PLOT: ; Plots a point of the circle, preserving HL and DE push hl push de - call __PLOT + call __PLOT pop de pop hl ret - + ENDP #line 469 "spfill.bas" - + #line 1 "pause.asm" + ; The PAUSE statement (Calling the ROM) - + __PAUSE: ld b, h ld c, l jp 1F3Dh ; PAUSE_1 #line 471 "spfill.bas" #line 1 "usr_str.asm" + ; 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) - - + + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -963,12 +973,13 @@ __PAUSE: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 10 "usr_str.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -976,25 +987,25 @@ __PAUSE: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1003,40 +1014,41 @@ __PAUSE: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1044,25 +1056,25 @@ __PAUSE: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1071,39 +1083,39 @@ __PAUSE: ; | (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 + ; 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 @@ -1111,56 +1123,56 @@ __PAUSE: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1169,57 +1181,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1228,47 +1240,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1278,27 +1290,27 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 11 "usr_str.asm" - + USR_STR: 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 11011111b ; Convert it to UPPER CASE @@ -1310,31 +1322,31 @@ USR_STR: 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 - + #line 472 "spfill.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/stoperr.asm b/tests/functional/stoperr.asm index e07508873..6d25cde12 100644 --- a/tests/functional/stoperr.asm +++ b/tests/functional/stoperr.asm @@ -35,19 +35,20 @@ __CALL_BACK__: ld c, l jp __END_PROGRAM #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -55,12 +56,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -68,14 +70,14 @@ __ERROR: __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 #line 26 "stoperr.bas" - + ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP diff --git a/tests/functional/storecstr.asm b/tests/functional/storecstr.asm index 444e9c921..c3343987c 100644 --- a/tests/functional/storecstr.asm +++ b/tests/functional/storecstr.asm @@ -40,6 +40,7 @@ __LABEL0: DEFB 6Ch DEFB 6Fh #line 1 "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$) @@ -50,13 +51,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -64,25 +67,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -91,51 +94,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -143,12 +147,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -156,7 +161,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -164,9 +169,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -174,25 +180,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -201,41 +207,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -243,25 +250,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -270,39 +277,39 @@ __STOP: ; | (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 + ; 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 @@ -310,57 +317,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -372,39 +379,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -412,15 +419,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -445,14 +452,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -460,24 +467,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -485,25 +493,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -512,38 +520,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -552,57 +560,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -611,47 +619,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -661,12 +669,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -685,29 +693,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -716,111 +724,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -836,7 +844,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -844,44 +852,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 28 "storecstr.bas" - + ZXBASIC_USER_DATA: _f: DEFB 00, 00 diff --git a/tests/functional/storef.asm b/tests/functional/storef.asm index 73461fd5c..0d2aa1e79 100644 --- a/tests/functional/storef.asm +++ b/tests/functional/storef.asm @@ -32,6 +32,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "storef.asm" + __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 @@ -39,7 +40,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -47,7 +48,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -59,9 +60,9 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 23 "storef.bas" - + ZXBASIC_USER_DATA: _f: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/storestr0.asm b/tests/functional/storestr0.asm index c5187874c..02473c296 100644 --- a/tests/functional/storestr0.asm +++ b/tests/functional/storestr0.asm @@ -33,6 +33,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "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$) @@ -43,13 +44,15 @@ __CALL_BACK__: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -57,25 +60,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -84,51 +87,52 @@ __CALL_BACK__: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -136,12 +140,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -149,7 +154,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -157,9 +162,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -167,25 +173,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -194,41 +200,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -236,25 +243,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -263,39 +270,39 @@ __STOP: ; | (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 + ; 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 @@ -303,57 +310,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -365,39 +372,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -405,15 +412,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -438,14 +445,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -453,24 +460,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -478,25 +486,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -505,38 +513,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -545,57 +553,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -604,47 +612,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -654,12 +662,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -678,29 +686,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -709,111 +717,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -829,7 +837,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -837,44 +845,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 21 "storestr0.bas" - + ZXBASIC_USER_DATA: _f: DEFB 00, 00 diff --git a/tests/functional/storestr1.asm b/tests/functional/storestr1.asm index 3c2e64b16..05c8899e9 100644 --- a/tests/functional/storestr1.asm +++ b/tests/functional/storestr1.asm @@ -70,9 +70,10 @@ __LABEL0: DEFB 6Ch DEFB 6Fh #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -80,25 +81,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -107,40 +108,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -148,25 +150,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -175,39 +177,39 @@ __LABEL0: ; | (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 + ; 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 @@ -215,56 +217,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -273,57 +275,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -332,47 +334,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -382,18 +384,20 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 58 "storestr1.bas" #line 1 "pstorestr.asm" + ; vim:ts=4:et:sw=4 - ; + ; ; 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 ; - + #line 1 "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$) @@ -404,13 +408,15 @@ __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 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -418,25 +424,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -445,51 +451,52 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -497,12 +504,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -510,7 +518,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -518,9 +526,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -528,25 +537,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -555,40 +564,40 @@ __STOP: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -600,39 +609,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -640,15 +649,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -673,14 +682,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -688,23 +697,23 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -723,29 +732,29 @@ __MEM_SUBTRACT: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -754,111 +763,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -874,7 +883,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -882,52 +891,52 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 8 "pstorestr.asm" - + __PSTORE_STR: push ix pop hl add hl, bc jp __STORE_STR - + #line 59 "storestr1.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/storestr2.asm b/tests/functional/storestr2.asm index d26eed880..acc935bd7 100644 --- a/tests/functional/storestr2.asm +++ b/tests/functional/storestr2.asm @@ -76,9 +76,10 @@ __LABEL0: DEFB 6Ch DEFB 6Fh #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -86,25 +87,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -113,40 +114,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -154,25 +156,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -181,39 +183,39 @@ __LABEL0: ; | (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 + ; 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 @@ -221,56 +223,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -279,57 +281,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -338,47 +340,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -388,15 +390,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 64 "storestr2.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -404,25 +408,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -431,50 +435,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -482,12 +487,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -495,16 +501,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -516,39 +522,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -556,15 +562,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -589,14 +595,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -604,25 +610,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -631,30 +637,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -662,13 +668,15 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 65 "storestr2.bas" #line 1 "pstorestr.asm" + ; vim:ts=4:et:sw=4 - ; + ; ; 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 ; - + #line 1 "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$) @@ -679,13 +687,15 @@ __LOADSTR: ; __FASTCALL__ entry ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -693,25 +703,25 @@ __LOADSTR: ; __FASTCALL__ entry ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -720,42 +730,42 @@ __LOADSTR: ; __FASTCALL__ entry ; | (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 + ; 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. @@ -774,29 +784,29 @@ __LOADSTR: ; __FASTCALL__ entry ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -805,111 +815,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -925,7 +935,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -933,52 +943,52 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 8 "pstorestr.asm" - + __PSTORE_STR: push ix pop hl add hl, bc jp __STORE_STR - + #line 66 "storestr2.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/storeu16.asm b/tests/functional/storeu16.asm index 13d8ae3b2..4e5ac9007 100644 --- a/tests/functional/storeu16.asm +++ b/tests/functional/storeu16.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _f: DEFB 00, 00 diff --git a/tests/functional/storeu32.asm b/tests/functional/storeu32.asm index 25ae9049e..b753e7d0f 100644 --- a/tests/functional/storeu32.asm +++ b/tests/functional/storeu32.asm @@ -30,7 +30,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _f: DEFB 00, 00, 00, 00 diff --git a/tests/functional/storeu8.asm b/tests/functional/storeu8.asm index fade56588..a30427b39 100644 --- a/tests/functional/storeu8.asm +++ b/tests/functional/storeu8.asm @@ -28,7 +28,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _f: DEFB 00 diff --git a/tests/functional/str0.asm b/tests/functional/str0.asm index ac916f697..f8c791150 100644 --- a/tests/functional/str0.asm +++ b/tests/functional/str0.asm @@ -72,9 +72,10 @@ __LABEL0: DEFW 0001h DEFB 31h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -82,25 +83,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -109,40 +110,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -150,25 +152,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -177,39 +179,39 @@ __LABEL0: ; | (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 + ; 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 @@ -217,56 +219,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -275,57 +277,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -334,47 +336,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -384,24 +386,27 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 59 "str0.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -409,45 +414,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -460,41 +466,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -502,12 +510,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -515,50 +524,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -567,15 +577,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -583,26 +595,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -616,44 +628,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -664,27 +677,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -692,27 +706,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -721,81 +736,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -804,32 +821,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -839,7 +856,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -847,19 +864,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -869,7 +887,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -880,18 +898,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -901,7 +920,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -912,18 +931,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -933,7 +953,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -946,21 +966,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -969,79 +990,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1050,75 +1071,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1128,7 +1149,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1140,16 +1161,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1163,49 +1184,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1216,17 +1237,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1239,27 +1260,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1267,10 +1288,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1287,80 +1308,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1377,8 +1398,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1402,17 +1423,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1423,7 +1444,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1433,21 +1454,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1466,9 +1487,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1493,63 +1514,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 60 "str0.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1558,21 +1580,23 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 61 "str0.bas" #line 1 "str.asm" + ; 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 - + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1580,25 +1604,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1607,40 +1631,40 @@ __PRINT_STR: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -1652,39 +1676,39 @@ __PRINT_STR: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -1692,15 +1716,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -1725,14 +1749,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -1740,52 +1764,53 @@ __MEM_SUBTRACT: 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 - - + + #line 8 "str.asm" #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -1801,171 +1826,173 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 9 "str.asm" - - + + __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 + 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 - + #line 62 "str0.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -1973,81 +2000,83 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 63 "str0.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -2056,35 +2085,35 @@ __I8TOFREG: 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 @@ -2092,20 +2121,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -2113,32 +2142,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 64 "str0.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/str00.asm b/tests/functional/str00.asm index 857b38a81..76145f3c5 100644 --- a/tests/functional/str00.asm +++ b/tests/functional/str00.asm @@ -37,6 +37,7 @@ __LABEL0: DEFB 35h DEFB 32h #line 1 "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$) @@ -47,13 +48,15 @@ __LABEL0: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -61,25 +64,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -88,51 +91,52 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -140,12 +144,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -153,7 +158,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -161,9 +166,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -171,25 +177,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -198,41 +204,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -240,25 +247,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -267,39 +274,39 @@ __STOP: ; | (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 + ; 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 @@ -307,57 +314,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -369,39 +376,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -409,15 +416,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -442,14 +449,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -457,24 +464,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -482,25 +490,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -509,38 +517,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -549,57 +557,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -608,47 +616,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -658,12 +666,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -682,29 +690,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -713,111 +721,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -833,7 +841,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -841,44 +849,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 25 "str00.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/str01.asm b/tests/functional/str01.asm index 84a4aaf71..ee41e42c8 100644 --- a/tests/functional/str01.asm +++ b/tests/functional/str01.asm @@ -37,17 +37,19 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -55,25 +57,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -82,40 +84,41 @@ __CALL_BACK__: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -123,25 +126,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -150,39 +153,39 @@ __CALL_BACK__: ; | (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 + ; 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 @@ -190,56 +193,56 @@ __CALL_BACK__: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -248,57 +251,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -307,47 +310,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -357,53 +360,55 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 25 "str01.bas" #line 1 "str.asm" + ; 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 - + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -411,25 +416,25 @@ __STORE_STR2: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -438,50 +443,51 @@ __STORE_STR2: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -489,12 +495,13 @@ __STORE_STR2: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -502,16 +509,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -523,39 +530,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -563,15 +570,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -596,14 +603,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -611,52 +618,53 @@ __MEM_SUBTRACT: 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 - - + + #line 8 "str.asm" #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -673,8 +681,9 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK jp __FPSTACK_PUSH #line 9 "str.asm" #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -682,109 +691,111 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 10 "str.asm" - + __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 + 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 - + #line 26 "str01.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -793,35 +804,35 @@ __I8TOFREG: 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 @@ -829,20 +840,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -850,32 +861,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 27 "str01.bas" - + ZXBASIC_USER_DATA: _b: DEFB 00 diff --git a/tests/functional/str02.asm b/tests/functional/str02.asm index 80ad56307..cca21b52b 100644 --- a/tests/functional/str02.asm +++ b/tests/functional/str02.asm @@ -52,9 +52,10 @@ __LABEL0: DEFB 6Fh DEFB 20h #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -62,25 +63,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -89,50 +90,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -140,12 +142,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -153,7 +156,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -161,9 +164,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -171,25 +175,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -198,39 +202,39 @@ __STOP: ; | (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 + ; 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 @@ -238,57 +242,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -300,39 +304,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -340,15 +344,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -373,14 +377,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -388,32 +392,34 @@ __MEM_SUBTRACT: 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 - - + + #line 40 "str02.bas" #line 1 "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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -421,25 +427,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -448,38 +454,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -488,57 +494,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -547,47 +553,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -597,82 +603,84 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 9 "storestr2.asm" - + __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 - + #line 41 "str02.bas" #line 1 "str.asm" + ; 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 - - + + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -689,8 +697,9 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK jp __FPSTACK_PUSH #line 9 "str.asm" #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -698,172 +707,174 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 10 "str.asm" - + __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 + 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 - + #line 42 "str02.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -871,81 +882,83 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 43 "str02.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -954,35 +967,35 @@ __I8TOFREG: 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 @@ -990,20 +1003,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -1011,32 +1024,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 44 "str02.bas" - + ZXBASIC_USER_DATA: _b: DEFB 02h diff --git a/tests/functional/str1.asm b/tests/functional/str1.asm new file mode 100644 index 000000000..b9eb10408 --- /dev/null +++ b/tests/functional/str1.asm @@ -0,0 +1,897 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + call __STORE_STR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 0001h + DEFB 31h +#line 1 "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 "strcpy.asm" + +#line 1 "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. + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 70 "realloc.asm" +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 71 "realloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 72 "realloc.asm" + + + ; --------------------------------------------------------------------- + ; 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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 24 "str1.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/str1.bas b/tests/functional/str1.bas new file mode 100644 index 000000000..e3f108e02 --- /dev/null +++ b/tests/functional/str1.bas @@ -0,0 +1,2 @@ +LET a$=STR 1 + diff --git a/tests/functional/stradd.asm b/tests/functional/stradd.asm index 187f174c5..741c93483 100644 --- a/tests/functional/stradd.asm +++ b/tests/functional/stradd.asm @@ -54,6 +54,7 @@ __LABEL1: DEFB 4Ch DEFB 44h #line 1 "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$) @@ -64,13 +65,15 @@ __LABEL1: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -78,25 +81,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -105,51 +108,52 @@ __LABEL1: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -157,12 +161,13 @@ __LABEL1: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -170,7 +175,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -178,9 +183,10 @@ __STOP: ret #line 70 "realloc.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -188,25 +194,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -215,41 +221,42 @@ __STOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -257,25 +264,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -284,39 +291,39 @@ __STOP: ; | (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 + ; 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 @@ -324,57 +331,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -386,39 +393,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -426,15 +433,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -459,14 +466,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -474,24 +481,25 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -499,25 +507,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -526,38 +534,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -566,57 +574,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -625,47 +633,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -675,12 +683,12 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 72 "realloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -699,29 +707,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -730,111 +738,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -850,7 +858,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -858,179 +866,182 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 42 "stradd.bas" #line 1 "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. - - - + + + __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 - + #line 43 "stradd.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -1038,50 +1049,50 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 44 "stradd.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/strbase.asm b/tests/functional/strbase.asm index 15ce5a912..cca27feea 100644 --- a/tests/functional/strbase.asm +++ b/tests/functional/strbase.asm @@ -64,19 +64,21 @@ __LABEL1: DEFW 0001h DEFB 6Fh #line 1 "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 -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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -84,25 +86,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -111,40 +113,41 @@ __LABEL1: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -152,25 +155,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -179,39 +182,39 @@ __LABEL1: ; | (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 + ; 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 @@ -219,56 +222,56 @@ __LABEL1: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -277,57 +280,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -336,47 +339,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -386,92 +389,92 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 11 "letsubstr.asm" - + __LETSUBSTR: PROC - + LOCAL __CONT0 LOCAL __CONT1 LOCAL __CONT2 LOCAL __FREE_STR LOCAL __FREE_STR0 - + 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 - + ld a, h or l jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather exx ; Return if p0 > p1 - + or a sbc hl, bc ; P0 >= String length? exx - + jp z, __FREE_STR0 ; Return if equal jp c, __FREE_STR0 ; Return if greather - + 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 subtring 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 (hl), ' ' ld d, h ld e, l inc de @@ -479,33 +482,33 @@ __CONT0: ; At this point DE = Length of subtring to copy 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 @@ -515,36 +518,38 @@ __CONT2: 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 hl pop de ldir ; Copy b$ into a$(x to y) - + exx ex de, hl - + __FREE_STR0: ex de, hl - + __FREE_STR: ex af, af' - or a ; If not 0, free + or a ; If not 0, free jp nz, __MEM_FREE ret - + ENDP - + #line 52 "strbase.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -552,25 +557,25 @@ __FREE_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -579,50 +584,51 @@ __FREE_STR: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -630,12 +636,13 @@ __FREE_STR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -643,16 +650,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -664,39 +671,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -704,15 +711,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -737,14 +744,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -752,25 +759,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -779,30 +786,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -810,6 +817,7 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 53 "strbase.bas" #line 1 "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$) @@ -820,13 +828,15 @@ __LOADSTR: ; __FASTCALL__ entry ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -834,25 +844,25 @@ __LOADSTR: ; __FASTCALL__ entry ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -861,42 +871,42 @@ __LOADSTR: ; __FASTCALL__ entry ; | (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 + ; 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. @@ -915,29 +925,29 @@ __LOADSTR: ; __FASTCALL__ entry ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -946,111 +956,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -1066,7 +1076,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -1074,141 +1084,144 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 54 "strbase.bas" #line 1 "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. - - - + + + __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 - + #line 55 "strbase.bas" #line 1 "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 + + ; 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 + ; it in dynamic memory if needed). Returns pointer (HL) to resulting ; string. NULL (0) if no memory for padding. ; - + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 18 "strslice.asm" - - - + + + __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) + 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 @@ -1216,19 +1229,19 @@ __STRSLICE_FAST: ; __FASTCALL__ Entry ld b, h ld c, l ; Copy to the end of str ccf ; Clears Carry flag for next subtraction - + __CONT: - ld h, b + 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 @@ -1237,15 +1250,15 @@ __CONT: 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 @@ -1258,26 +1271,26 @@ __CONT: 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 - + ret + + ENDP + #line 56 "strbase.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/strbase2.asm b/tests/functional/strbase2.asm index 5947f1de3..81309c320 100644 --- a/tests/functional/strbase2.asm +++ b/tests/functional/strbase2.asm @@ -73,19 +73,21 @@ __LABEL1: DEFW 0001h DEFB 6Fh #line 1 "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 -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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -93,25 +95,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -120,40 +122,41 @@ __LABEL1: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -161,25 +164,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -188,39 +191,39 @@ __LABEL1: ; | (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 + ; 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 @@ -228,56 +231,56 @@ __LABEL1: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -286,57 +289,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -345,47 +348,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -395,92 +398,92 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 11 "letsubstr.asm" - + __LETSUBSTR: PROC - + LOCAL __CONT0 LOCAL __CONT1 LOCAL __CONT2 LOCAL __FREE_STR LOCAL __FREE_STR0 - + 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 - + ld a, h or l jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather exx ; Return if p0 > p1 - + or a sbc hl, bc ; P0 >= String length? exx - + jp z, __FREE_STR0 ; Return if equal jp c, __FREE_STR0 ; Return if greather - + 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 subtring 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 (hl), ' ' ld d, h ld e, l inc de @@ -488,33 +491,33 @@ __CONT0: ; At this point DE = Length of subtring to copy 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 @@ -524,36 +527,38 @@ __CONT2: 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 hl pop de ldir ; Copy b$ into a$(x to y) - + exx ex de, hl - + __FREE_STR0: ex de, hl - + __FREE_STR: ex af, af' - or a ; If not 0, free + or a ; If not 0, free jp nz, __MEM_FREE ret - + ENDP - + #line 60 "strbase2.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -561,25 +566,25 @@ __FREE_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -588,50 +593,51 @@ __FREE_STR: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -639,12 +645,13 @@ __FREE_STR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -652,16 +659,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -673,39 +680,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -713,15 +720,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -746,14 +753,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -761,25 +768,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -788,30 +795,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -819,19 +826,22 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 61 "strbase2.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -839,45 +849,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -890,65 +901,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -957,15 +970,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -973,26 +988,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -1006,44 +1021,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -1054,27 +1070,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -1082,27 +1099,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -1111,81 +1129,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1194,32 +1214,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1229,7 +1249,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1237,19 +1257,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1259,7 +1280,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1270,18 +1291,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1291,7 +1313,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1302,18 +1324,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1323,7 +1346,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1336,21 +1359,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1359,79 +1383,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1440,75 +1464,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1518,7 +1542,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1530,16 +1554,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1553,49 +1577,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1606,17 +1630,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1629,27 +1653,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1657,10 +1681,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1677,80 +1701,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1767,8 +1791,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1792,17 +1816,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1813,7 +1837,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1823,21 +1847,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1856,9 +1880,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1883,63 +1907,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 62 "strbase2.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1948,11 +1973,12 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 63 "strbase2.bas" #line 1 "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$) @@ -1963,13 +1989,15 @@ __PRINT_STR: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1977,25 +2005,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -2004,42 +2032,42 @@ __PRINT_STR: ; | (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 + ; 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. @@ -2058,29 +2086,29 @@ __PRINT_STR: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -2089,111 +2117,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -2209,7 +2237,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -2217,141 +2245,144 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 64 "strbase2.bas" #line 1 "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. - - - + + + __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 - + #line 65 "strbase2.bas" #line 1 "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 + + ; 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 + ; it in dynamic memory if needed). Returns pointer (HL) to resulting ; string. NULL (0) if no memory for padding. ; - + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 18 "strslice.asm" - - - + + + __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) + 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 @@ -2359,19 +2390,19 @@ __STRSLICE_FAST: ; __FASTCALL__ Entry ld b, h ld c, l ; Copy to the end of str ccf ; Clears Carry flag for next subtraction - + __CONT: - ld h, b + 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 @@ -2380,15 +2411,15 @@ __CONT: 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 @@ -2401,26 +2432,26 @@ __CONT: 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 - + ret + + ENDP + #line 66 "strbase2.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/strict.bas b/tests/functional/strict.bas new file mode 100644 index 000000000..e88b8f935 --- /dev/null +++ b/tests/functional/strict.bas @@ -0,0 +1,5 @@ +#pragma strict=false +DIM b +#pragma strict=true +DIM a + diff --git a/tests/functional/strict2.asm b/tests/functional/strict2.asm new file mode 100644 index 000000000..9ea78030e --- /dev/null +++ b/tests/functional/strict2.asm @@ -0,0 +1,43 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_anysub: + push ix + ld ix, 0 + add ix, sp +_anysub__leave: + ld sp, ix + pop ix + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/strict2.bas b/tests/functional/strict2.bas new file mode 100644 index 000000000..23dec08a1 --- /dev/null +++ b/tests/functional/strict2.bas @@ -0,0 +1,6 @@ +#pragma strict=true + +sub anysub() +end sub + + diff --git a/tests/functional/strict3.bas b/tests/functional/strict3.bas new file mode 100644 index 000000000..db40491f4 --- /dev/null +++ b/tests/functional/strict3.bas @@ -0,0 +1,7 @@ +#pragma strict=true + +REM error, anyfunc has no type +function anyfunc() +end function + + diff --git a/tests/functional/strict4.bas b/tests/functional/strict4.bas new file mode 100644 index 000000000..d981dae49 --- /dev/null +++ b/tests/functional/strict4.bas @@ -0,0 +1,10 @@ +#pragma strict=true + +SUB goodsub(a as Ubyte, b as UInteger) +END SUB + +REM no error: no param missing type +REM error: param missing type +SUB anysub(a as UByte, b) +END SUB + diff --git a/tests/functional/strict5.bas b/tests/functional/strict5.bas new file mode 100644 index 000000000..89cd93aed --- /dev/null +++ b/tests/functional/strict5.bas @@ -0,0 +1,7 @@ +#pragma strict=true + +DIM a, b as Byte +DIM c +DIM e, f + + diff --git a/tests/functional/strict6.bas b/tests/functional/strict6.bas new file mode 100644 index 000000000..acaafc041 --- /dev/null +++ b/tests/functional/strict6.bas @@ -0,0 +1,6 @@ + +#pragma strict=true + +DIM a = 5 +CONST b = 6 + diff --git a/tests/functional/strict7.bas b/tests/functional/strict7.bas new file mode 100644 index 000000000..643a03694 --- /dev/null +++ b/tests/functional/strict7.bas @@ -0,0 +1,11 @@ + + +DIM a(1 TO 3) + +#pragma strict=true + +DIM b(1 TO 3) + +DIM c(1 TO 3) => {1, 2, 3} + + diff --git a/tests/functional/string_substr.asm b/tests/functional/string_substr.asm new file mode 100644 index 000000000..a3d5cc5bb --- /dev/null +++ b/tests/functional/string_substr.asm @@ -0,0 +1,779 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, __LABEL0 + push hl + ld a, (_i) + inc a + ld l, a + ld h, 0 + push hl + ld hl, 65534 + push hl + xor a + call __STRSLICE + ex de, hl + ld hl, _a + call __STORE_STR2 + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 0006h + DEFB 53h + DEFB 54h + DEFB 52h + DEFB 49h + DEFB 4Eh + DEFB 47h +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 9 "storestr2.asm" + +__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 + +#line 40 "string_substr.bas" +#line 1 "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 "strlen.asm" + + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL + +__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 + + +#line 18 "strslice.asm" +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 19 "strslice.asm" + + +__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 + +#line 41 "string_substr.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 +_a: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/string_substr.bas b/tests/functional/string_substr.bas new file mode 100644 index 000000000..3bb9f004b --- /dev/null +++ b/tests/functional/string_substr.bas @@ -0,0 +1,3 @@ +DIM i As UByte +LET a$ = "STRING"(i + 1 TO) + diff --git a/tests/functional/stringfunc.asm b/tests/functional/stringfunc.asm index f6aff1d26..692afd682 100644 --- a/tests/functional/stringfunc.asm +++ b/tests/functional/stringfunc.asm @@ -50,9 +50,10 @@ __LABEL0: DEFB 6Ch DEFB 6Fh #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -60,25 +61,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -87,40 +88,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -128,25 +130,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -155,39 +157,39 @@ __LABEL0: ; | (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 + ; 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 @@ -195,56 +197,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -253,57 +255,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -312,47 +314,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -362,15 +364,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 38 "stringfunc.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -378,25 +382,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -405,50 +409,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -456,12 +461,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -469,16 +475,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -490,39 +496,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -530,15 +536,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -563,14 +569,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -578,25 +584,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -605,37 +611,37 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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 #line 39 "stringfunc.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/stringparam.asm b/tests/functional/stringparam.asm index 686a10899..6cb244078 100644 --- a/tests/functional/stringparam.asm +++ b/tests/functional/stringparam.asm @@ -66,9 +66,10 @@ __LABEL0: DEFB 6Ch DEFB 6Fh #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -76,25 +77,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -103,40 +104,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -144,25 +146,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -171,39 +173,39 @@ __LABEL0: ; | (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 + ; 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 @@ -211,56 +213,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -269,57 +271,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -328,47 +330,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -378,15 +380,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 53 "stringparam.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -394,25 +398,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -421,50 +425,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -472,12 +477,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -485,16 +491,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -506,39 +512,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -546,15 +552,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -579,14 +585,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -594,25 +600,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -621,30 +627,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -652,19 +658,22 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 54 "stringparam.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -672,45 +681,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -723,65 +733,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -790,15 +802,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -806,26 +820,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -839,44 +853,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -887,27 +902,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -915,27 +931,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -944,81 +961,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1027,32 +1046,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1062,7 +1081,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1070,19 +1089,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1092,7 +1112,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1103,18 +1123,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1124,7 +1145,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1135,18 +1156,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1156,7 +1178,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1169,21 +1191,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1192,79 +1215,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1273,75 +1296,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1351,7 +1374,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1363,16 +1386,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1386,49 +1409,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1439,17 +1462,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1462,27 +1485,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1490,10 +1513,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1510,80 +1533,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1600,8 +1623,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1625,17 +1648,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1646,7 +1669,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1656,21 +1679,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1689,9 +1712,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1716,63 +1739,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 55 "stringparam.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1781,11 +1805,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 56 "stringparam.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/strlocal0.asm b/tests/functional/strlocal0.asm index 6b3f746d4..9104b31bd 100644 --- a/tests/functional/strlocal0.asm +++ b/tests/functional/strlocal0.asm @@ -70,9 +70,10 @@ __LABEL0: DEFB 6Ch DEFB 64h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -80,25 +81,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -107,40 +108,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -148,25 +150,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -175,39 +177,39 @@ __LABEL0: ; | (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 + ; 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 @@ -215,56 +217,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -273,57 +275,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -332,47 +334,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -382,24 +384,27 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 57 "strlocal0.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -407,45 +412,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -458,41 +464,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -500,12 +508,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -513,50 +522,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -565,15 +575,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -581,26 +593,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -614,44 +626,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -662,27 +675,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -690,27 +704,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -719,81 +734,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -802,32 +819,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -837,7 +854,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -845,19 +862,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -867,7 +885,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -878,18 +896,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -899,7 +918,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -910,18 +929,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -931,7 +951,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -944,21 +964,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -967,79 +988,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1048,75 +1069,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1126,7 +1147,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1138,16 +1159,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1161,49 +1182,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1214,17 +1235,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1237,27 +1258,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1265,10 +1286,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1285,80 +1306,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1375,8 +1396,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1400,17 +1421,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1421,7 +1442,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1431,21 +1452,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1464,9 +1485,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1491,63 +1512,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 58 "strlocal0.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1556,18 +1578,20 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 59 "strlocal0.bas" #line 1 "pstorestr.asm" + ; vim:ts=4:et:sw=4 - ; + ; ; 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 ; - + #line 1 "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$) @@ -1578,13 +1602,15 @@ __PRINT_STR: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1592,25 +1618,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1619,42 +1645,43 @@ __PRINT_STR: ; | (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 + ; 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 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1662,25 +1689,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1689,40 +1716,40 @@ __PRINT_STR: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -1734,39 +1761,39 @@ __PRINT_STR: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -1774,15 +1801,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -1807,14 +1834,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -1822,23 +1849,23 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -1857,29 +1884,29 @@ __MEM_SUBTRACT: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -1888,111 +1915,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -2008,7 +2035,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -2016,52 +2043,52 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 8 "pstorestr.asm" - + __PSTORE_STR: push ix pop hl add hl, bc jp __STORE_STR - + #line 60 "strlocal0.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/strparam0.asm b/tests/functional/strparam0.asm index e5f1b4010..059207f67 100644 --- a/tests/functional/strparam0.asm +++ b/tests/functional/strparam0.asm @@ -96,9 +96,10 @@ __LABEL0: DEFB 6Ch DEFB 64h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -106,25 +107,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -133,40 +134,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -174,25 +176,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -201,39 +203,39 @@ __LABEL0: ; | (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 + ; 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 @@ -241,56 +243,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -299,57 +301,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -358,47 +360,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -408,15 +410,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 83 "strparam0.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -424,25 +428,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -451,50 +455,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -502,12 +507,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -515,16 +521,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -536,39 +542,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -576,15 +582,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -609,14 +615,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -624,25 +630,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -651,30 +657,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -682,19 +688,22 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 84 "strparam0.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -702,45 +711,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -753,65 +763,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -820,15 +832,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -836,26 +850,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -869,44 +883,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -917,27 +932,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -945,27 +961,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -974,81 +991,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1057,32 +1076,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1092,7 +1111,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1100,19 +1119,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1122,7 +1142,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1133,18 +1153,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1154,7 +1175,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1165,18 +1186,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1186,7 +1208,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1199,21 +1221,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1222,79 +1245,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1303,75 +1326,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1381,7 +1404,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1393,16 +1416,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1416,49 +1439,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1469,17 +1492,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1492,27 +1515,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1520,10 +1543,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1540,80 +1563,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1630,8 +1653,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1655,17 +1678,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1676,7 +1699,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1686,21 +1709,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1719,9 +1742,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1746,63 +1769,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 85 "strparam0.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1811,11 +1835,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 86 "strparam0.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/strparam1.asm b/tests/functional/strparam1.asm index db9053a02..1f35397a7 100644 --- a/tests/functional/strparam1.asm +++ b/tests/functional/strparam1.asm @@ -80,9 +80,10 @@ __LABEL0: DEFB 6Fh DEFB 20h #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -90,25 +91,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -117,50 +118,51 @@ __LABEL0: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -168,12 +170,13 @@ __LABEL0: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -181,7 +184,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -189,9 +192,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -199,25 +203,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -226,39 +230,39 @@ __STOP: ; | (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 + ; 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 @@ -266,57 +270,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -328,39 +332,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -368,15 +372,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -401,14 +405,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -416,24 +420,25 @@ __MEM_SUBTRACT: 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 - - + + #line 68 "strparam1.bas" #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -441,25 +446,25 @@ __MEM_SUBTRACT: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -468,38 +473,38 @@ __MEM_SUBTRACT: ; | (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 + ; 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 @@ -508,57 +513,57 @@ __MEM_SUBTRACT: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -567,47 +572,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -617,18 +622,20 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 69 "strparam1.bas" #line 1 "pstorestr.asm" + ; vim:ts=4:et:sw=4 - ; + ; ; 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 ; - + #line 1 "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$) @@ -639,13 +646,15 @@ __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 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -653,25 +662,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -680,42 +689,42 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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. @@ -734,29 +743,29 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -765,111 +774,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -885,7 +894,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -893,91 +902,93 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 8 "pstorestr.asm" - + __PSTORE_STR: push ix pop hl add hl, bc jp __STORE_STR - + #line 70 "strparam1.bas" #line 1 "str.asm" + ; 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 - - + + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -994,8 +1005,9 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK jp __FPSTACK_PUSH #line 9 "str.asm" #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -1003,172 +1015,174 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 10 "str.asm" - + __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 + 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 - + #line 71 "strparam1.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -1176,81 +1190,83 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 72 "strparam1.bas" #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -1259,35 +1275,35 @@ __I8TOFREG: 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 @@ -1295,20 +1311,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -1316,32 +1332,32 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 73 "strparam1.bas" - + ZXBASIC_USER_DATA: _b: DEFB 02h diff --git a/tests/functional/strparam2.asm b/tests/functional/strparam2.asm index 22d51f398..96e4cd6a3 100644 --- a/tests/functional/strparam2.asm +++ b/tests/functional/strparam2.asm @@ -83,19 +83,22 @@ __LABEL0: DEFB 49h DEFB 43h #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -103,45 +106,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -154,41 +158,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -196,12 +202,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -209,50 +216,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -261,15 +269,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -277,26 +287,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -310,44 +320,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -358,27 +369,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -386,27 +398,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -415,81 +428,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -498,32 +513,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -533,7 +548,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -541,19 +556,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -563,7 +579,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -574,18 +590,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -595,7 +612,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -606,18 +623,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -627,7 +645,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -640,21 +658,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -663,79 +682,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -744,75 +763,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -822,7 +841,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -834,16 +853,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -857,49 +876,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -910,17 +929,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -933,27 +952,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -961,10 +980,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -981,80 +1000,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1071,8 +1090,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1096,17 +1115,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1117,7 +1136,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1127,21 +1146,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1160,9 +1179,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1187,25 +1206,27 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 70 "strparam2.bas" #line 1 "printstr.asm" - - - + + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1213,25 +1234,25 @@ PRINT_EOL_ATTR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1240,40 +1261,41 @@ PRINT_EOL_ATTR: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1281,25 +1303,25 @@ PRINT_EOL_ATTR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1308,39 +1330,39 @@ PRINT_EOL_ATTR: ; | (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 + ; 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 @@ -1348,56 +1370,56 @@ PRINT_EOL_ATTR: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1406,57 +1428,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1465,47 +1487,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1515,51 +1537,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 5 "printstr.asm" - + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1568,11 +1590,12 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 71 "strparam2.bas" #line 1 "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$) @@ -1583,13 +1606,15 @@ __PRINT_STR: ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1597,25 +1622,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1624,42 +1649,43 @@ __PRINT_STR: ; | (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 + ; 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 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1667,25 +1693,25 @@ __PRINT_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1694,40 +1720,40 @@ __PRINT_STR: ; | (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 + ; 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_ALLOC ; Allocates a block of memory in the heap. @@ -1739,39 +1765,39 @@ __PRINT_STR: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -1779,15 +1805,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -1812,14 +1838,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -1827,23 +1853,23 @@ __MEM_SUBTRACT: 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 - - + + #line 71 "realloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. @@ -1862,29 +1888,29 @@ __MEM_SUBTRACT: ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -1893,111 +1919,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -2013,7 +2039,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -2021,44 +2047,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 72 "strparam2.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/strparam3.asm b/tests/functional/strparam3.asm index a7a241eb1..80c2db5d7 100644 --- a/tests/functional/strparam3.asm +++ b/tests/functional/strparam3.asm @@ -90,9 +90,10 @@ __LABEL0: DEFB 49h DEFB 43h #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -100,25 +101,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -127,40 +128,41 @@ __LABEL0: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -168,25 +170,25 @@ __LABEL0: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -195,39 +197,39 @@ __LABEL0: ; | (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 + ; 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 @@ -235,56 +237,56 @@ __LABEL0: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -293,57 +295,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -352,47 +354,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -402,15 +404,17 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 77 "strparam3.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -418,25 +422,25 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -445,50 +449,51 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -496,12 +501,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -509,16 +515,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -530,39 +536,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -570,15 +576,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -603,14 +609,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -618,25 +624,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -645,30 +651,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -676,19 +682,22 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 78 "strparam3.bas" #line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes - + #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -696,45 +705,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -747,65 +757,67 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - - - + + + + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -814,15 +826,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -830,26 +844,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -863,44 +877,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -911,27 +926,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -939,27 +955,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -968,81 +985,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -1051,32 +1070,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -1086,7 +1105,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -1094,19 +1113,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -1116,7 +1136,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -1127,18 +1147,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -1148,7 +1169,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -1159,18 +1180,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -1180,7 +1202,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -1193,21 +1215,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -1216,79 +1239,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -1297,75 +1320,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -1375,7 +1398,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -1387,16 +1410,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -1410,49 +1433,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -1463,17 +1486,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -1486,27 +1509,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -1514,10 +1537,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -1534,80 +1557,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1624,8 +1647,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1649,17 +1672,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1670,7 +1693,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1680,21 +1703,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1713,9 +1736,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1740,63 +1763,64 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 5 "print_eol_attr.asm" - - + + PRINT_EOL_ATTR: call PRINT_EOL jp COPY_ATTR #line 79 "strparam3.bas" #line 1 "printstr.asm" - - - - - + + + + + + ; PRINT command routine ; Prints string pointed by HL - + 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 @@ -1805,11 +1829,11 @@ __PRINT_STR: push hl ; Push str address for later ld d, a ; Saves a FLAG jp __PRINT_STR_LOOP - + ENDP - + #line 80 "strparam3.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/strsigil.asm b/tests/functional/strsigil.asm index 253001735..e732a4ce3 100644 --- a/tests/functional/strsigil.asm +++ b/tests/functional/strsigil.asm @@ -47,11 +47,13 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "asc.asm" + ; Returns the ascii code for the given str #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -59,25 +61,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -86,40 +88,41 @@ __CALL_BACK__: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -127,25 +130,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -154,39 +157,39 @@ __CALL_BACK__: ; | (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 + ; 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 @@ -194,56 +197,56 @@ __CALL_BACK__: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -252,57 +255,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -311,47 +314,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -361,89 +364,92 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 3 "asc.asm" - + __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 #line 35 "strsigil.bas" #line 1 "ftou32reg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "ftou32reg.asm" - + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -451,102 +457,121 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 36 "strsigil.bas" #line 1 "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 + + ; 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 + ; it in dynamic memory if needed). Returns pointer (HL) to resulting ; string. NULL (0) if no memory for padding. ; - + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 18 "strslice.asm" #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -554,25 +579,25 @@ __STRLEN: ; Direct FASTCALL entry ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -581,50 +606,51 @@ __STRLEN: ; Direct FASTCALL entry ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -632,12 +658,13 @@ __STRLEN: ; Direct FASTCALL entry ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -645,16 +672,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -666,39 +693,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/zxbasic/library-asm/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 @@ -706,15 +733,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -739,14 +766,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -754,41 +781,41 @@ __MEM_SUBTRACT: 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 - - + + #line 19 "strslice.asm" - - + + __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) + 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 @@ -796,19 +823,19 @@ __STRSLICE_FAST: ; __FASTCALL__ Entry ld b, h ld c, l ; Copy to the end of str ccf ; Clears Carry flag for next subtraction - + __CONT: - ld h, b + 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 @@ -817,15 +844,15 @@ __CONT: 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 @@ -838,26 +865,26 @@ __CONT: 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 - + ret + + ENDP + #line 37 "strsigil.bas" - + ZXBASIC_USER_DATA: _e3: DEFB 00 diff --git a/tests/functional/sub16.asm b/tests/functional/sub16.asm index 2b632518c..e4d0a8338 100644 --- a/tests/functional/sub16.asm +++ b/tests/functional/sub16.asm @@ -46,7 +46,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/sub16a.asm b/tests/functional/sub16a.asm index 91493915c..a3dc1cc12 100644 --- a/tests/functional/sub16a.asm +++ b/tests/functional/sub16a.asm @@ -34,7 +34,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/sub16b.asm b/tests/functional/sub16b.asm index 134721fbc..c2f89948f 100644 --- a/tests/functional/sub16b.asm +++ b/tests/functional/sub16b.asm @@ -42,7 +42,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/sub8.asm b/tests/functional/sub8.asm index f81f9f9ed..3d64a8278 100644 --- a/tests/functional/sub8.asm +++ b/tests/functional/sub8.asm @@ -42,7 +42,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/sub8a.asm b/tests/functional/sub8a.asm index 410f62f65..71679c05b 100644 --- a/tests/functional/sub8a.asm +++ b/tests/functional/sub8a.asm @@ -32,7 +32,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/sub8b.asm b/tests/functional/sub8b.asm index f76121722..2397e2d7c 100644 --- a/tests/functional/sub8b.asm +++ b/tests/functional/sub8b.asm @@ -38,7 +38,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/functional/subf00.asm b/tests/functional/subf00.asm index 003cced54..648d39094 100644 --- a/tests/functional/subf00.asm +++ b/tests/functional/subf00.asm @@ -35,12 +35,13 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -61,10 +62,11 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - + + #line 26 "subf00.bas" #line 1 "storef.asm" + __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 @@ -72,7 +74,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -80,7 +82,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -92,41 +94,43 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 27 "subf00.bas" #line 1 "subf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -142,31 +146,31 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "subf.asm" - + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - - + + __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 - + #line 28 "subf00.bas" - + ZXBASIC_USER_DATA: _a: DEFB 80h diff --git a/tests/functional/subf01.asm b/tests/functional/subf01.asm index 390a54090..3d8eee9b9 100644 --- a/tests/functional/subf01.asm +++ b/tests/functional/subf01.asm @@ -39,6 +39,7 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "storef.asm" + __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 @@ -46,7 +47,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -54,7 +55,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -66,41 +67,43 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 30 "subf01.bas" #line 1 "subf.asm" + #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -116,31 +119,31 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 2 "subf.asm" - + ; ------------------------------------------------------------- ; 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 ; ------------------------------------------------------------- - - + + __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 - + #line 31 "subf01.bas" - + ZXBASIC_USER_DATA: _a: DEFB 80h diff --git a/tests/functional/subf16c.asm b/tests/functional/subf16c.asm index 44a74e750..b494f34c6 100644 --- a/tests/functional/subf16c.asm +++ b/tests/functional/subf16c.asm @@ -59,53 +59,55 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 50 "subf16c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 51 "subf16c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 00h diff --git a/tests/functional/subi32c.asm b/tests/functional/subi32c.asm index ba4bc4c57..14fdf300e 100644 --- a/tests/functional/subi32c.asm +++ b/tests/functional/subi32c.asm @@ -68,53 +68,55 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 59 "subi32c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 60 "subi32c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/subparam.asm b/tests/functional/subparam.asm new file mode 100644 index 000000000..7359b9357 --- /dev/null +++ b/tests/functional/subparam.asm @@ -0,0 +1,55 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 87 + push af + ld a, 127 + push af + call _test + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_test: + push ix + ld ix, 0 + add ix, sp + ld a, (ix+7) + ld (ix+5), a +_test__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/subparam.bas b/tests/functional/subparam.bas new file mode 100644 index 000000000..73bebc947 --- /dev/null +++ b/tests/functional/subparam.bas @@ -0,0 +1,9 @@ + + +SUB test(x as UByte, y as Ubyte) + Let x = y +END SUB + +REM calls a sub with no parameters +test 127, 87 + diff --git a/tests/functional/subrec.asm b/tests/functional/subrec.asm new file mode 100644 index 000000000..25ef05074 --- /dev/null +++ b/tests/functional/subrec.asm @@ -0,0 +1,2174 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + call __PRINT_INIT + call CLS + ld a, 1 + ld (_x), a + jp __LABEL0 +__LABEL3: + ld de, 0 + ld hl, 1 + ld (_result), hl + ld (_result + 2), de + ld a, (_x) + ld l, a + ld h, 0 + ld e, h + ld d, h + push de + push hl + call _fact + ld hl, __LABEL5 + xor a + call __PRINTSTR + ld a, (_x) + call __PRINTU8 + ld hl, __LABEL6 + xor a + call __PRINTSTR + ld hl, (_result) + ld de, (_result + 2) + call __PRINTU32 + call PRINT_EOL +__LABEL4: + ld hl, _x + inc (hl) +__LABEL0: + ld a, 10 + ld hl, (_x - 1) + cp h + jp nc, __LABEL3 +__LABEL2: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_fact: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld de, 0 + ld hl, 2 + call __SUB32 + jp c, _fact__leave +__LABEL8: + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld hl, (_result + 2) + push hl + ld hl, (_result) + push hl + pop hl + pop de + call __MUL32 + ld (_result), hl + ld (_result + 2), de + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld de, 0 + ld hl, 1 + call __SUB32 + push de + push hl + call _fact +_fact__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +__LABEL5: + DEFW 0006h + DEFB 46h + DEFB 61h + DEFB 63h + DEFB 74h + DEFB 20h + DEFB 28h +__LABEL6: + DEFW 0004h + DEFB 29h + DEFB 20h + DEFB 3Dh + DEFB 20h +#line 1 "cls.asm" + + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + + ;CLS EQU 0DAFh + + ; Our faster implementation + +#line 1 "sposn.asm" + + ; Printing positioning library. + PROC + LOCAL ECHO_E + +__LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. + ld de, (S_POSN) + ld hl, (MAXX) + or a + sbc hl, de + ex de, hl + ret + + +__SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. + ld hl, (MAXX) + or a + sbc hl, de + ld (S_POSN), hl ; saves it again + ret + + + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 + + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y + + ENDP + +#line 9 "cls.asm" + +CLS: + PROC + + LOCAL COORDS + LOCAL __CLS_SCR + LOCAL ATTR_P + LOCAL SCREEN + + ld hl, 0 + ld (COORDS), hl + ld hl, 1821h + ld (S_POSN), hl +__CLS_SCR: + ld hl, SCREEN + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6144 + ldir + + ; Now clear attributes + + ld a, (ATTR_P) + ld (hl), a + ld bc, 767 + ldir + ret + + COORDS EQU 23677 + SCREEN EQU 16384 ; Default start of the screen (can be changed) + ATTR_P EQU 23693 + ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address + + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines + ; to get the start of the screen + ENDP + +#line 121 "subrec.bas" +#line 1 "mul32.asm" + +#line 1 "_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 + + +__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: + and a ; reset carry flag + sbc hl,hl ; result bits 32..47 = 0 + exx + sbc hl,hl ; result bits 48..63 = 0 + 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 + +#line 2 "mul32.asm" + +__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 + + +#line 122 "subrec.bas" +#line 1 "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 "in_screen.asm" + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 3 "in_screen.asm" + +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) + + PROC + LOCAL __IN_SCREEN_ERR + + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + + ld a, d + inc hl + cp (hl) + ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ;; ret + 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 +#line 8 "print.asm" +#line 1 "table_jump.asm" + + +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) + +#line 9 "print.asm" +#line 1 "ink.asm" + + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + +#line 1 "const.asm" + + ; Global constants + + P_FLAG EQU 23697 + FLAGS2 EQU 23681 + ATTR_P EQU 23693 ; permanet ATTRIBUTES + ATTR_T EQU 23695 ; temporary ATTRIBUTES + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + UDG EQU 23675 ; Pointer to UDG Charset + MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars + +#line 5 "ink.asm" + +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 + +#line 10 "print.asm" +#line 1 "paper.asm" + + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + + +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 + +#line 11 "print.asm" +#line 1 "flash.asm" + + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +FLASH: + ld de, ATTR_P +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH + +#line 12 "print.asm" +#line 1 "bright.asm" + + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + + + +BRIGHT: + ld de, ATTR_P + +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 + + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret + + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT + +#line 13 "print.asm" +#line 1 "over.asm" + + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register +#line 1 "copy_attr.asm" + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + +COPY_ATTR: + ; Just copies current permanent attribs to 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 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret + + ENDP + +#line 4 "over.asm" + + +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 + +#line 14 "print.asm" +#line 1 "inverse.asm" + + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + + + +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 + +#line 15 "print.asm" +#line 1 "bold.asm" + + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + + +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 + +#line 16 "print.asm" +#line 1 "italic.asm" + + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + + +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 + +#line 17 "print.asm" + +#line 1 "attr.asm" + + ; Attribute routines +; vim:ts=4:et:sw: + + + + + + + +__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 + 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 + + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) + add hl, de + + ld de, (SCREEN_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 + +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + + call __ATTR_ADDR + 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 + + +#line 19 "print.asm" + + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY + + xor a + ld (FLAGS2), a + + ret + + +__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_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones + + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + + call __LOAD_S_POSN + + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) + + 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 + add hl, de ; HL = Screen address + DE + ex de, hl ; DE = Screen address + + ex af, af' + + cp 80h ; Is it an UDG or a ? + jp c, __SRCADDR + + cp 90h + jp nc, __PRINT_UDG + + ; Print a 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) + jp __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 + bit 4, (iy + $47) + call nz, __ITALIC + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be ALWAYS 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 opertions: + ; NORMAL: 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + call __LOAD_S_POSN + push de + call __SET_ATTR + pop de + inc e ; COL = COL + 1 + ld hl, (MAXX) + ld a, e + dec l ; l = MAXX + cp l ; Lower than max? + jp c, __PRINT_CONT; Nothing to do + call __PRINT_EOL1 + exx ; counteracts __PRINT_EOL1 exx + jp __PRINT_CONT2 + +__PRINT_CONT: + call __SAVE_S_POSN + +__PRINT_CONT2: + exx + ret + + ; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + 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) + call __LOAD_S_POSN + +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 + +__PRINT_EOL2: + ld a, d + inc a + +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a + +__PRINT_EOL_END: + ld d, a + +__PRINT_AT2_END: + call __SAVE_S_POSN + 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 + jp __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + ld (PRINT_JUMP_STATE), hl + ret + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + push hl + push de + push bc + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + call PRINT_TAB + pop bc + pop de + pop hl + ret + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE + +__PRINT_AT: + ld hl, __PRINT_AT1 + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + exx + ld hl, __PRINT_AT2 + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + jp __PRINT_AT1_END + +__PRINT_AT2: + exx + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + call __LOAD_S_POSN + ld e, a + ld hl, (MAXX) + cp (hl) + jr c, __PRINT_AT2_END + jr __PRINT_EOL1 + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jp nz, __PRINT_AT2_END + ld hl, (MAXX) + ld e, l + dec e + dec e + dec d + cp d + jp nz, __PRINT_AT2_END + ld d, h + dec d + jp __PRINT_AT2_END + +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE + +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE + +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE + +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE + +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE + +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART + +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART + +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART + + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + + +__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 + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE + + inc a + call __LOAD_S_POSN ; e = current row + ld d, a + ld a, e + cp 21h + jr nz, CONTINUE + ld e, -1 +CONTINUE: + ld a, d + inc e + sub e ; A = A - E + and 31 ; + ret z ; Already at position E + 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 __BOLD + LOCAL __BOLD_LOOP + LOCAL __ITALIC + LOCAL __PRINT_EOL1 + LOCAL __PRINT_EOL2 + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_AT2_END + LOCAL __PRINT_BOLD + LOCAL __PRINT_BOLD2 + LOCAL __PRINT_ITA + LOCAL __PRINT_ITA2 + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +__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 + + +#line 123 "subrec.bas" +#line 1 "printstr.asm" + + + + +#line 1 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 5 "printstr.asm" + + ; PRINT command routine + ; Prints string pointed by HL + +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 + +#line 124 "subrec.bas" +#line 1 "printu32.asm" + +#line 1 "printi32.asm" + +#line 1 "printnum.asm" + + + + +__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 mus preserve registers + ld a, '-' + jp __PRINT_DIGIT + + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + + +#line 2 "printi32.asm" +#line 1 "neg32.asm" + +__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 + +#line 3 "printi32.asm" +#line 1 "div32.asm" + + + + ; --------------------------------------------------------- +__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 + +#line 4 "printi32.asm" + + + +__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 + +#line 2 "printu32.asm" + +#line 125 "subrec.bas" +#line 1 "printu8.asm" + +#line 1 "printi8.asm" + + +#line 1 "div8.asm" + + ; -------------------------------- +__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 + +#line 3 "printi8.asm" + +__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 + +#line 2 "printu8.asm" + +#line 126 "subrec.bas" +#line 1 "sub32.asm" + + ; 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 + +__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 +#line 127 "subrec.bas" + +ZXBASIC_USER_DATA: +_result: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +_x: + DEFB 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/subrec.bas b/tests/functional/subrec.bas new file mode 100644 index 000000000..cba3f8ba9 --- /dev/null +++ b/tests/functional/subrec.bas @@ -0,0 +1,20 @@ +REM Factorial recursive test + +DIM result as ulong = 1 + +sub fact(x as ulong) + if x < 2 then + return + end if + + result = result * x + fact(x - 1) +end sub + +cls +for x = 1 To 10: + result = 1 + fact(x) + print "Fact ("; x; ") = "; result +next x + diff --git a/tests/functional/substr_empty.asm b/tests/functional/substr_empty.asm new file mode 100644 index 000000000..aa668fe9e --- /dev/null +++ b/tests/functional/substr_empty.asm @@ -0,0 +1,896 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, (_b) + ld hl, _a + call __STORE_STR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "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 "strcpy.asm" + +#line 1 "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. + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 70 "realloc.asm" +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 71 "realloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 72 "realloc.asm" + + + ; --------------------------------------------------------------------- + ; 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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 21 "substr_empty.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00 +_b: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/substr_empty.bas b/tests/functional/substr_empty.bas new file mode 100644 index 000000000..00311e71d --- /dev/null +++ b/tests/functional/substr_empty.bas @@ -0,0 +1,3 @@ + +a$=b$(TO) + diff --git a/tests/functional/substr_empty2.asm b/tests/functional/substr_empty2.asm new file mode 100644 index 000000000..6f7486df2 --- /dev/null +++ b/tests/functional/substr_empty2.asm @@ -0,0 +1,902 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, _a + call __STORE_STR + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 0006h + DEFB 53h + DEFB 54h + DEFB 52h + DEFB 49h + DEFB 4Eh + DEFB 47h +#line 1 "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 "strcpy.asm" + +#line 1 "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. + + +#line 1 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 70 "realloc.asm" +#line 1 "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 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 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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 70 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 71 "realloc.asm" +#line 1 "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 + ; --------------------------------------------------------------------- + +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 + +#line 72 "realloc.asm" + + + ; --------------------------------------------------------------------- + ; 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. + ; + ; --------------------------------------------------------------------- +__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 + +#line 2 "strcpy.asm" + + ; String library + + +__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 + +#line 14 "storestr.asm" + +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc + +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + + 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 elemem ptr + + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + +#line 29 "substr_empty2.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/substr_empty2.bas b/tests/functional/substr_empty2.bas new file mode 100644 index 000000000..a045f54ca --- /dev/null +++ b/tests/functional/substr_empty2.bas @@ -0,0 +1,2 @@ +a$="STRING"() + diff --git a/tests/functional/substr_expr.asm b/tests/functional/substr_expr.asm new file mode 100644 index 000000000..3250032d9 --- /dev/null +++ b/tests/functional/substr_expr.asm @@ -0,0 +1,902 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld de, __LABEL0 + ld hl, (_a) + call __ADDSTR + push hl + ld hl, 5 + push hl + ld hl, 65534 + push hl + ld a, 1 + call __STRSLICE + ex de, hl + ld hl, _b + call __STORE_STR2 + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 0001h + DEFB 2Eh +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 9 "storestr2.asm" + +__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 + +#line 34 "substr_expr.bas" +#line 1 "strcat.asm" + +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 2 "strcat.asm" +#line 1 "strlen.asm" + + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL + +__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 + + +#line 3 "strcat.asm" + +__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 + +#line 35 "substr_expr.bas" +#line 1 "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. + ; + + + + + +__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 + +#line 36 "substr_expr.bas" + +ZXBASIC_USER_DATA: +_b: + DEFB 00, 00 +_a: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/substr_expr.bas b/tests/functional/substr_expr.bas new file mode 100644 index 000000000..c40764a66 --- /dev/null +++ b/tests/functional/substr_expr.bas @@ -0,0 +1,3 @@ + +LET b$ = (a$ + ".")(5 TO ) + diff --git a/tests/functional/substr_expr2.asm b/tests/functional/substr_expr2.asm new file mode 100644 index 000000000..b212d3477 --- /dev/null +++ b/tests/functional/substr_expr2.asm @@ -0,0 +1,780 @@ + org 32768 + ; Defines HEAP SIZE +ZXBASIC_HEAP_SIZE EQU 4768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + call __MEM_INIT + ld hl, __LABEL0 + push hl + ld hl, (_i) + ld de, 5 + add hl, de + push hl + ld hl, (_i) + ld de, 5 + add hl, de + push hl + xor a + call __STRSLICE + ex de, hl + ld hl, _a + call __STORE_STR2 + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +__LABEL0: + DEFW 0006h + DEFB 53h + DEFB 54h + DEFB 52h + DEFB 49h + DEFB 4Eh + DEFB 47h +#line 1 "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 "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 "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 + ; --------------------------------------------------------------------- +__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 + +#line 69 "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 + ; --------------------------------------------------------------------- + +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 + +#line 9 "storestr2.asm" + +__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 + +#line 41 "substr_expr2.bas" +#line 1 "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 "strlen.asm" + + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL + +__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 + + +#line 18 "strslice.asm" +#line 1 "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 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 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 "error.asm" + + ; Simple error control routines +; vim:ts=4:et: + + 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 +#line 69 "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) + ; --------------------------------------------------------------------- + +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 111 "/src/zxb/trunk/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/src/zxb/trunk/library-asm/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 + + +#line 19 "strslice.asm" + + +__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 + +#line 42 "substr_expr2.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00, 00 +_a: + DEFB 00, 00 +ZXBASIC_MEM_HEAP: + ; Defines DATA END +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/substr_expr2.bas b/tests/functional/substr_expr2.bas new file mode 100644 index 000000000..b7eaa9eed --- /dev/null +++ b/tests/functional/substr_expr2.bas @@ -0,0 +1,4 @@ + +DIM i As Uinteger +LET a$ = "STRING"(5 + i) + diff --git a/tests/functional/substr_expr_err.bas b/tests/functional/substr_expr_err.bas new file mode 100644 index 000000000..841804678 --- /dev/null +++ b/tests/functional/substr_expr_err.bas @@ -0,0 +1,3 @@ +DIM a as Byte +LET c$ = (a)(5 TO ) + diff --git a/tests/functional/substrlval.asm b/tests/functional/substrlval.asm index d5d817aeb..7697cecf5 100644 --- a/tests/functional/substrlval.asm +++ b/tests/functional/substrlval.asm @@ -58,19 +58,21 @@ __LABEL1: DEFW 0001h DEFB 7Ah #line 1 "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 -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 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -78,25 +80,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -105,40 +107,41 @@ __LABEL1: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -146,25 +149,25 @@ __LABEL1: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -173,39 +176,39 @@ __LABEL1: ; | (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 + ; 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 @@ -213,56 +216,56 @@ __LABEL1: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -271,57 +274,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -330,47 +333,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -380,92 +383,92 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 11 "letsubstr.asm" - + __LETSUBSTR: PROC - + LOCAL __CONT0 LOCAL __CONT1 LOCAL __CONT2 LOCAL __FREE_STR LOCAL __FREE_STR0 - + 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 - + ld a, h or l jp z, __FREE_STR0 ; 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 requester 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_STR0 ; Return if greather exx ; Return if p0 > p1 - + or a sbc hl, bc ; P0 >= String length? exx - + jp z, __FREE_STR0 ; Return if equal jp c, __FREE_STR0 ; Return if greather - + 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 subtring 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 (hl), ' ' ld d, h ld e, l inc de @@ -473,33 +476,33 @@ __CONT0: ; At this point DE = Length of subtring to copy 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 @@ -509,36 +512,38 @@ __CONT2: 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 hl pop de ldir ; Copy b$ into a$(x to y) - + exx ex de, hl - + __FREE_STR0: ex de, hl - + __FREE_STR: ex af, af' - or a ; If not 0, free + or a ; If not 0, free jp nz, __MEM_FREE ret - + ENDP - + #line 46 "substrlval.bas" #line 1 "loadstr.asm" + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -546,25 +551,25 @@ __FREE_STR: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -573,50 +578,51 @@ __FREE_STR: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -624,12 +630,13 @@ __FREE_STR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -637,16 +644,16 @@ __ERROR: __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 #line 69 "alloc.asm" - - - + + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -658,39 +665,39 @@ __STOP: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -698,15 +705,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -731,14 +738,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -746,25 +753,25 @@ __MEM_SUBTRACT: 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 - - + + #line 2 "loadstr.asm" - + ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again ; Finally, it returns result pointer in HL - + __ILOADSTR: ; This is the indirect pointer entry HL = (HL) ld a, h or l @@ -773,30 +780,30 @@ __ILOADSTR: ; This is the indirect pointer entry HL = (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) @@ -804,6 +811,7 @@ __LOADSTR: ; __FASTCALL__ entry ret #line 47 "substrlval.bas" #line 1 "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$) @@ -814,13 +822,15 @@ __LOADSTR: ; __FASTCALL__ entry ; ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ - - + + #line 1 "strcpy.asm" + #line 1 "realloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -828,25 +838,25 @@ __LOADSTR: ; __FASTCALL__ entry ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -855,42 +865,42 @@ __LOADSTR: ; __FASTCALL__ entry ; | (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 + ; 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. @@ -909,29 +919,29 @@ __LOADSTR: ; __FASTCALL__ entry ; the new size is adjusted. If BC < original length, the content ; will be truncated. Otherwise, extra block content might contain ; memory garbage. - ; + ; ; --------------------------------------------------------------------- __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 @@ -940,111 +950,111 @@ __REALLOC: ; Reallocates block pointed by HL, with new length 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 - + #line 2 "strcpy.asm" - + ; String library - - + + __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 + 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 - + 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 @@ -1060,7 +1070,7 @@ __STRCPY: ; Copies string pointed by HL into string pointed by DE ldir pop hl ret - + __NOTHING_TO_COPY: ex de, hl ld (hl), e @@ -1068,44 +1078,44 @@ __NOTHING_TO_COPY: ld (hl), d dec hl ret - + ENDP - + #line 14 "storestr.asm" - + __PISTORE_STR: ; Indirect assignement at (IX + BC) push ix pop hl add hl, bc - + __ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address - + 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 elemem ptr - + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret - + #line 48 "substrlval.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/subu32c.asm b/tests/functional/subu32c.asm index 8f774b4d8..5de5e8617 100644 --- a/tests/functional/subu32c.asm +++ b/tests/functional/subu32c.asm @@ -68,53 +68,55 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "sub32.asm" - ; SUB32 + + ; 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 - + __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 #line 59 "subu32c.bas" #line 1 "swap32.asm" + ; Exchanges current DE HL with the ; ones in the stack - + __SWAP32: pop bc ; Return address ex (sp), hl - dec sp - dec sp + inc sp + inc sp ex de, hl ex (sp), hl ex de, hl - inc sp - inc sp + dec sp + dec sp push bc ret - + #line 60 "subu32c.bas" - + ZXBASIC_USER_DATA: _level: DEFB 01h diff --git a/tests/functional/swap32.asm b/tests/functional/swap32.asm new file mode 100644 index 000000000..3e11789ce --- /dev/null +++ b/tests/functional/swap32.asm @@ -0,0 +1,245 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld de, 0 + ld hl, 10 + call __SWAP32 + call __DIVU32 + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "div32.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "div32.asm" + + ; --------------------------------------------------------- +__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 + +#line 28 "swap32.bas" +#line 1 "swap32.asm" + + ; Exchanges current DE HL with the + ; ones in the stack + +__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 + +#line 29 "swap32.bas" + +ZXBASIC_USER_DATA: +_a: + DEFB 0FFh + DEFB 0FFh + DEFB 00h + DEFB 00h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/swap32.bas b/tests/functional/swap32.bas new file mode 100644 index 000000000..7388a29c6 --- /dev/null +++ b/tests/functional/swap32.bas @@ -0,0 +1,4 @@ +DIM a as ULong = 65535 + +LET a = a / 10 + diff --git a/tests/functional/tap_00.tap b/tests/functional/tap_00.tap index 77269b083..250677210 100644 Binary files a/tests/functional/tap_00.tap and b/tests/functional/tap_00.tap differ diff --git a/tests/functional/tap_01.bas b/tests/functional/tap_01.bas new file mode 100644 index 000000000..9aadbfa2a --- /dev/null +++ b/tests/functional/tap_01.bas @@ -0,0 +1,4 @@ +#pragma org=0 + +DIM a as UByte = 1 + diff --git a/tests/functional/tap_01.tap b/tests/functional/tap_01.tap new file mode 100644 index 000000000..62471c0a6 Binary files /dev/null and b/tests/functional/tap_01.tap differ diff --git a/tests/functional/tap_incbin.bas b/tests/functional/tap_incbin.bas new file mode 100644 index 000000000..6c35f50c1 --- /dev/null +++ b/tests/functional/tap_incbin.bas @@ -0,0 +1,5 @@ + +ASM +incbin "haplofnt.bin" +END ASM + diff --git a/tests/functional/tap_incbin.tap b/tests/functional/tap_incbin.tap new file mode 100644 index 000000000..8b57bf118 Binary files /dev/null and b/tests/functional/tap_incbin.tap differ diff --git a/tests/functional/test.py b/tests/functional/test.py index a10475445..84e2359e5 100755 --- a/tests/functional/test.py +++ b/tests/functional/test.py @@ -12,7 +12,7 @@ reOPT = re.compile(r'^opt([0-9]+)_') # To detect -On tests -reBIN = re.compile(r'^(tzx|tap)_') # To detect tzx / tap test +reBIN = re.compile(r'^(?:.*/)?(tzx|tap)_.*') # To detect tzx / tap test EXIT_CODE = 0 FILTER = r'^(([ \t]*;)|(#[ \t]*line))' @@ -43,7 +43,8 @@ FOUT = sys.stdout # Output file. By default stdout but can be captured changing this TEMP_DIR = None QUIET = False # True so suppress output (useful for testing) -STDERR = '/dev/stderr' +DEFAULT_STDERR = '/dev/stderr' +STDERR = None INLINE = True # Set to false to use system Shell @@ -196,7 +197,7 @@ def _get_testbas_options(fname): - the test .asm file that will be generated - the extension of the file (normally .asm) """ - prep = ['-e', '/dev/null'] if CLOSE_STDERR else [] + prep = ['-e', '/dev/null'] if CLOSE_STDERR else ['-e', STDERR] options = ['-O1'] match = reOPT.match(getName(fname)) @@ -206,52 +207,106 @@ def _get_testbas_options(fname): match = reBIN.match(getName(fname)) if match and match.groups()[0].lower() in ('tzx', 'tap'): ext = match.groups()[0].lower() - tfname = os.path.join(TEMP_DIR, getName(fname) + os.extsep + ext) - options.extend(['--%s' % ext, fname, '-o', tfname] + prep) + if not UPDATE: + tfname = os.path.join(TEMP_DIR, getName(fname) + os.extsep + ext) + else: + tfname = os.path.join(os.path.dirname(fname), getName(fname) + os.extsep + ext) + options.extend(['--%s' % ext, fname, '-o', tfname, '-a', '-B'] + prep) else: ext = 'asm' if not UPDATE: - tfname = os.path.join(TEMP_DIR, 'test' + fname + os.extsep + ext) + tfname = os.path.join(TEMP_DIR, 'test' + getName(fname) + os.extsep + ext) else: - tfname = getName(fname) + os.extsep + ext + tfname = os.path.join(os.path.dirname(fname), getName(fname) + os.extsep + ext) options.extend(['--asm', fname, '-o', tfname] + prep) - return options, tfname, ext -def testPREPRO(fname, pattern_=None): +def updateTest(tfname, pattern_): + if not os.path.exists(tfname): + return # was deleted -> The test is an error test and no compile filed should exist + + if reBIN.match(tfname): # Binary files do not need updating + return + + lines = get_file_lines(tfname, replace_regexp=pattern_, replace_what=ZXBASIC_ROOT, + replace_with=_original_root) + with zxb.api.utils.open_file(tfname, 'wt', encoding='utf-8') as f: + f.write(''.join(lines)) + + +def testPREPRO(fname, pattern_=None, inline=None): + """ Test preprocessing file. Test is done by preprocessing the file and then + comparing the output against an expected one. The output file can optionally be filtered + using a filter_ regexp (see above). + + :param fname: Filename (usually a .bi file) to test. + :param filter_: regexp for filtering output before comparing. It will be ignored for binary (tzx, tap, etc) files + :param inline: whether the test should be run inline or using the system shell + :return: True on success false if not + """ global UPDATE - tfname = os.path.join(TEMP_DIR, 'test' + fname + os.extsep + 'out') - prep = ' 2> /dev/null' if CLOSE_STDERR else '' - okfile = getName(fname) + os.extsep + 'out' - OPTIONS = '' - match = reOPT.match(getName(fname)) - if match: - OPTIONS = '-O' + match.groups()[0] + if inline is None: + inline = INLINE + + tfname = os.path.join(TEMP_DIR, 'test' + getName(fname) + os.extsep + 'out') + okfile = os.path.join(os.path.dirname(fname), getName(fname) + os.extsep + 'out') + + if UPDATE: + tfname = okfile + if os.path.exists(okfile): + os.unlink(okfile) + prep = ['-e', '/dev/null'] if CLOSE_STDERR else ['-e', STDERR] if UPDATE: tfname = okfile + if os.path.exists(okfile): + os.unlink(okfile) + + options = [os.path.basename(fname), '-o', tfname] + prep + if inline: + func = lambda: zxbpp.entry_point(options) + else: + cmdline = '{0} {1}'.format(ZXBPP, ' '.join(options)) + func = lambda: systemExec(cmdline) - syscmd = '{0} {1} {2} > {3}{4}'.format(ZXBPP, OPTIONS, fname, tfname, prep) result = None - with TempTestFile(lambda: systemExec(syscmd), tfname, UPDATE) as err_lvl: - if not UPDATE and not err_lvl: - result = is_same_file(okfile, tfname, replace_regexp=pattern_, - replace_what=ZXBASIC_ROOT, replace_with=_original_root) + try: + current_path = os.getcwd() + os.chdir(os.path.dirname(fname) or os.curdir) + + with TempTestFile(func, tfname, UPDATE): + if not UPDATE: + result = is_same_file(okfile, tfname, replace_regexp=pattern_, + replace_what=ZXBASIC_ROOT, replace_with=_original_root) + else: + updateTest(tfname, pattern_) + finally: + os.chdir(current_path) + return result def testASM(fname, inline=None): + """ Test assembling an ASM (.asm) file. Test is done by assembling the source code into a binary and then + comparing the output file against an expected binary output. + + :param fname: Filename (.asm file) to test. + :param inline: whether the test should be run inline or using the system shell + :return: True on success false if not + """ if inline is None: inline = INLINE - tfname = os.path.join(TEMP_DIR, 'test' + fname + os.extsep + 'bin') + tfname = os.path.join(TEMP_DIR, 'test' + getName(fname) + os.extsep + 'bin') prep = ['-e', '/dev/null'] if CLOSE_STDERR else ['-e', STDERR] - okfile = getName(fname) + os.extsep + 'bin' + okfile = os.path.join(os.path.dirname(fname), getName(fname) + os.extsep + 'bin') if UPDATE: tfname = okfile + if os.path.exists(okfile): + os.unlink(okfile) options = [fname, '-o', tfname] + prep @@ -283,10 +338,14 @@ def testBAS(fname, filter_=None, inline=None): inline = INLINE options, tfname, ext = _get_testbas_options(fname) - okfile = getName(fname) + os.extsep + ext + okfile = os.path.join(os.path.dirname(fname), getName(fname) + os.extsep + ext) + + if UPDATE and os.path.exists(okfile): + os.unlink(okfile) if inline: - func = lambda: zxb.main(options + ['-I', ZXBASIC_ROOT]) + func = lambda: zxb.main(options + ['-I', ':'.join(os.path.join(ZXBASIC_ROOT, x) + for x in ('library', 'library-asm'))]) else: syscmd = '{0} {1}'.format(ZXB, ' '.join(options)) func = lambda: systemExec(syscmd) @@ -295,6 +354,8 @@ def testBAS(fname, filter_=None, inline=None): with TempTestFile(func, tfname, UPDATE): if not UPDATE: result = is_same_file(okfile, tfname, filter_, is_binary=reBIN.match(fname) is not None) + else: + updateTest(tfname, FILTER) return result @@ -306,20 +367,21 @@ def testFiles(file_list): COUNTER = 0 for fname in file_list: + fname = fname ext = getExtension(fname) if ext == 'asm': - if os.path.exists(getName(fname) + os.extsep + 'bas'): + if os.path.exists(os.path.join(os.path.dirname(fname), getName(fname) + os.extsep + 'bas')): continue # Ignore asm files which have a .bas since they're test results result = testASM(fname, inline=INLINE) elif ext == 'bas': result = testBAS(fname, filter_=FILTER, inline=INLINE) elif ext == 'bi': - result = testPREPRO(fname, pattern_=FILTER) + result = testPREPRO(fname, pattern_=FILTER, inline=INLINE) else: result = None COUNTER += 1 - _msg(("%4i " % COUNTER) + fname + ':') + _msg(("%4i " % COUNTER) + getName(fname) + ':') if result: _msg('ok \r') @@ -392,7 +454,7 @@ def normalizeDiff(diff): is_same_file(fname1, tfname, ignore_regexp=FILTER, diff=lines) lines = normalizeDiff(lines) - if lines != fdiff: + if lines[:len(fdiff)] != fdiff: for x, y in zip(lines, fdiff): x = x.strip() y = y.strip() @@ -436,22 +498,32 @@ def main(argv=None): global QUIET global STDERR global INLINE + global CLOSE_STDERR + global COUNTER + global FAILED + global EXIT_CODE + + COUNTER = FAILED = EXIT_CODE = 0 parser = argparse.ArgumentParser(description='Test compiler output against source code samples') parser.add_argument('-d', '--show-diff', action='store_true', help='Shows output difference on failure') parser.add_argument('-v', '--show-visual-diff', action='store_true', help='Shows visual difference using vimdiff ' 'upon failure') - parser.add_argument('-u', '--update', type=str, default=None, help='Updates all *.bas test if the UPDATE diff' - ' matches') + parser.add_argument('-u', '--update', type=str, default=None, help='Updates a test if the UPDATE diff matches') parser.add_argument('-U', '--force-update', action='store_true', help='Updates all failed test with the new output') parser.add_argument('--tmp-dir', type=str, default=TEMP_DIR, help='Temporary directory for tests generation') parser.add_argument('FILES', nargs='+', type=str, help='List of files to be processed') parser.add_argument('-q', '--quiet', action='store_true', help='Run quietly, suppressing normal output') - parser.add_argument('-e', '--stderr', type=str, default=STDERR, help='File for stderr messages') + parser.add_argument('-e', '--stderr', type=str, default=None, help='File for stderr messages') parser.add_argument('-S', '--use-shell', action='store_true', help='Use system shell for test instead of inline') args = parser.parse_args(argv) STDERR = args.stderr + if STDERR: + CLOSE_STDERR = False + else: + STDERR = DEFAULT_STDERR + INLINE = not args.use_shell temp_dir_created = False diff --git a/tests/functional/test_.py b/tests/functional/test_.py index 8c0e5fbe1..270d9400c 100755 --- a/tests/functional/test_.py +++ b/tests/functional/test_.py @@ -8,89 +8,6 @@ import test -__doc__ = """ ->>> process_file('doloop1.bas') -doloop1.bas:2: warning: Infinite empty loop ->>> process_file('dountil1.bas') -dountil1.bas:2: warning: Condition is always False -dountil1.bas:2: warning: Empty loop ->>> process_file('doloop2.bas') -doloop2.bas:4: warning: Using default implicit type 'ubyte' for 'a' -doloop2.bas:5: warning: Condition is always True -doloop2.bas:8: warning: Condition is always True -doloop2.bas:12: warning: Condition is always False -doloop2.bas:4: warning: Variable 'a' is never used ->>> process_file('dowhile1.bas') -dowhile1.bas:1: warning: Condition is always True -dowhile1.bas:1: warning: Empty loop ->>> process_file('subcall1.bas') -subcall1.bas:6: 'test' is SUBROUTINE not a FUNCTION ->>> process_file('subcall2.bas') -subcall2.bas:6: 'test' is a SUBROUTINE, not a FUNCTION ->>> process_file('prepro05.bi') -prepro05.bi:3: warning: "test" redefined (previous definition at prepro05.bi:2) ->>> process_file('prepro07.bi') -prepro07.bi:2: Error: Duplicated name parameter "x" ->>> process_file('prepro28.bi') -prepro28.bi:3: Error: invalid directive #defien ->>> process_file('param3.bas') -param3.bas:3: warning: Parameter 's' is never used -param3.bas:5: Function 'test' (previously declared at 3) type mismatch -param3.bas:6: Type Error: Function must return a numeric value, not a string ->>> process_file('typecast1.bas') -typecast1.bas:5: Cannot convert value to string. Use STR() function ->>> process_file('typecast2.bas') -typecast2.bas:1: warning: Parameter 'c' is never used -typecast2.bas:10: Cannot convert string to a value. Use VAL() function ->>> process_file('jr1.asm') -jr1.asm:12: Relative jump out of range ->>> process_file('jr2.asm') -jr2.asm:2: Relative jump out of range ->>> process_file('mcleod3.bas') -mcleod3.bas:3: 'GenerateSpaces' is neither an array nor a function. -mcleod3.bas:1: warning: Parameter 'path' is never used -mcleod3.bas:6: warning: Parameter 'n' is never used ->>> process_file('poke3.bas') -poke3.bas:4: Variable 'a' is an array and cannot be used in this context ->>> process_file('poke5.bas') -poke5.bas:4: Variable 'a' is an array and cannot be used in this context ->>> process_file('arrlabels10.bas') -arrlabels10.bas:3: warning: Using default implicit type 'float' for 'a' -arrlabels10.bas:3: Can't convert non-numeric value to float at compile time -arrlabels10.bas:3: Can't convert non-numeric value to float at compile time ->>> process_file('arrlabels10c.bas') -arrlabels10c.bas:3: Can't convert non-numeric value to string at compile time -arrlabels10c.bas:3: Can't convert non-numeric value to string at compile time ->>> process_file('arrlabels10d.bas') -arrlabels10d.bas:3: Undeclared array "a" ->>> process_file('arrlabels11.bas') -arrlabels11.bas:4: Initializer expression is not constant. ->>> process_file('lexerr.bas') -lexerr.bas:1: ignoring illegal character '%' -lexerr.bas:1: Syntax Error. Unexpected token '1.0' ->>> process_file('opt2_nogoto.bas') -opt2_nogoto.bas:2: Undeclared label "nolabel" ->>> process_file('nosub.bas') -nosub.bas:3: function 'nofunc' declared but not implemented ->>> process_file('incbin0.asm') -incbin0.asm:3: cannot read file 'nofile.bin' ->>> process_file('align3.asm') -align3.asm:2: ALIGN value must be greater than 1 ->>> process_file('rst0.asm') -rst0.asm:2: Invalid RST number 1 ->>> process_file('im0.asm') -im0.asm:2: Invalid IM number 3 ->>> process_file('orgbad.asm') -orgbad.asm:2: Memory ORG out of range [0 .. 65535]. Current value: -1 ->>> process_file('defsbad.asm') -defsbad.asm:2: too many arguments for DEFS -""" - - -def process_file(fname): - test.main(['-S', '-q', fname]) - - class OutputProxy(six.StringIO): """A simple interface to replace sys.stdout so doctest can capture it. @@ -102,14 +19,33 @@ def flush(self): sys.stdout.flush() -def main(): +def process_file(fname, params=None): + if params is None: + params = ['-S', '-q'] + try: + current_path = os.path.abspath(os.getcwd()) test.set_temp_dir() test.FOUT = OutputProxy() - doctest.testmod() + if os.path.dirname(fname): + os.chdir(os.path.abspath(os.path.dirname(fname))) + fname = os.path.basename(fname) + else: + os.chdir(os.path.realpath(os.path.dirname(__file__))) + test.main(params + [fname]) + os.chdir(current_path) finally: os.rmdir(test.TEMP_DIR) + test.TEMP_DIR = None + + +def main(): + current_path = os.path.abspath(os.getcwd()) + os.chdir(os.path.realpath(os.path.dirname(__file__) or os.curdir)) + result = doctest.testfile('test_errmsg.txt') # evaluates to True on failure + os.chdir(current_path) + return int(result.failed) if __name__ == '__main__': - main() + sys.exit(main()) diff --git a/tests/functional/test_asm.py b/tests/functional/test_asm.py new file mode 100755 index 000000000..2b0613c33 --- /dev/null +++ b/tests/functional/test_asm.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import test +import os +import sys +import pytest + + +TEST_PATH = os.path.dirname(os.path.realpath(__file__)) + + +@pytest.mark.parametrize('fname', [os.path.join(TEST_PATH, f) for f in os.listdir(TEST_PATH) if f.endswith(".asm")]) +def test_asm(fname): + test.main(['-d', '-e', '/dev/null', fname]) + if test.COUNTER == 0: + return + + sys.stderr.write("Total: %i, Failed: %i (%3.2f%%)\n" % + (test.COUNTER, test.FAILED, 100.0 * test.FAILED / float(test.COUNTER))) + + assert test.EXIT_CODE == 0, "ASM program test failed" diff --git a/tests/functional/test_basic.py b/tests/functional/test_basic.py new file mode 100755 index 000000000..90c204429 --- /dev/null +++ b/tests/functional/test_basic.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import test +import os +import sys +import pytest + + +TEST_PATH = os.path.dirname(os.path.realpath(__file__)) + + +@pytest.mark.parametrize('fname', [os.path.join(TEST_PATH, f) for f in os.listdir(TEST_PATH) if f.endswith(".bas")]) +@pytest.mark.timeout(15) +def test_basic(fname): + test.main(['-d', '-e', '/dev/null', fname]) + if test.COUNTER == 0: + return + sys.stderr.write("Total: %i, Failed: %i (%3.2f%%)\n" % + (test.COUNTER, test.FAILED, 100.0 * test.FAILED / float(test.COUNTER))) + + assert test.EXIT_CODE == 0, "BASIC program test failed" diff --git a/tests/functional/test_errmsg.txt b/tests/functional/test_errmsg.txt new file mode 100644 index 000000000..73fa01dfe --- /dev/null +++ b/tests/functional/test_errmsg.txt @@ -0,0 +1,142 @@ +>>> from test_ import process_file +>>> process_file('doloop1.bas') +doloop1.bas:2: warning: Infinite empty loop +>>> process_file('dountil1.bas') +dountil1.bas:2: warning: Condition is always False +dountil1.bas:2: warning: Empty loop +>>> process_file('doloop2.bas') +doloop2.bas:4: warning: Using default implicit type 'ubyte' for 'a' +doloop2.bas:5: warning: Condition is always True +doloop2.bas:8: warning: Condition is always True +doloop2.bas:12: warning: Condition is always False +doloop2.bas:4: warning: Variable 'a' is never used +>>> process_file('dowhile1.bas') +dowhile1.bas:1: warning: Condition is always True +dowhile1.bas:1: warning: Empty loop +>>> process_file('subcall1.bas') +subcall1.bas:6: 'test' is SUBROUTINE not a FUNCTION +>>> process_file('subcall2.bas') +subcall2.bas:6: 'test' is a SUBROUTINE, not a FUNCTION +>>> process_file('prepro05.bi') +prepro05.bi:3: warning: "test" redefined (previous definition at prepro05.bi:2) +>>> process_file('prepro07.bi') +prepro07.bi:2: Error: Duplicated name parameter "x" +>>> process_file('prepro28.bi') +prepro28.bi:3: Error: invalid directive #defien +>>> process_file('param3.bas') +param3.bas:3: warning: Parameter 's' is never used +param3.bas:5: Function 'test' (previously declared at 3) type mismatch +param3.bas:6: Type Error: Function must return a numeric value, not a string +>>> process_file('typecast1.bas') +typecast1.bas:5: Cannot convert value to string. Use STR() function +>>> process_file('typecast2.bas') +typecast2.bas:1: warning: Parameter 'c' is never used +typecast2.bas:10: Cannot convert string to a value. Use VAL() function +>>> process_file('jr1.asm') +jr1.asm:12: Relative jump out of range +>>> process_file('jr2.asm') +jr2.asm:2: Relative jump out of range +>>> process_file('mcleod3.bas') +mcleod3.bas:3: 'GenerateSpaces' is neither an array nor a function. +mcleod3.bas:1: warning: Parameter 'path' is never used +mcleod3.bas:6: warning: Parameter 'n' is never used +>>> process_file('poke3.bas') +poke3.bas:4: Variable 'a' is an array and cannot be used in this context +>>> process_file('poke5.bas') +poke5.bas:4: Variable 'a' is an array and cannot be used in this context +>>> process_file('arrlabels10.bas') +arrlabels10.bas:3: warning: Using default implicit type 'float' for 'a' +arrlabels10.bas:3: Can't convert non-numeric value to float at compile time +>>> process_file('arrlabels10c.bas') +arrlabels10c.bas:3: Can't convert non-numeric value to string at compile time +>>> process_file('arrlabels10d.bas') +arrlabels10d.bas:3: Undeclared array "a" +>>> process_file('arrlabels11.bas') +arrlabels11.bas:4: Initializer expression is not constant. +>>> process_file('lexerr.bas') +lexerr.bas:1: ignoring illegal character '%' +lexerr.bas:1: warning: Using default implicit type 'float' for 'a' +lexerr.bas:1: Syntax Error. Unexpected token '%' +>>> process_file('opt2_nogoto.bas') +opt2_nogoto.bas:2: Undeclared label "nolabel" +>>> process_file('nosub.bas') +nosub.bas:3: function 'nofunc' declared but not implemented +>>> process_file('incbin0.asm') +incbin0.asm:3: Error: file 'nofile.bin' not found +>>> process_file('align3.asm') +align3.asm:2: ALIGN value must be greater than 1 +>>> process_file('rst0.asm') +rst0.asm:2: Invalid RST number 1 +>>> process_file('im0.asm') +im0.asm:2: Invalid IM number 3 +>>> process_file('orgbad.asm') +orgbad.asm:2: Memory ORG out of range [0 .. 65535]. Current value: -1 +>>> process_file('defsbad.asm') +defsbad.asm:2: too many arguments for DEFS +>>> process_file('asmprepro.asm') +asmprepro.asm:8: warning: Recursive inclusion +asmprepro.asm:12: warning: Recursive inclusion +>>> process_file('strict.bas') +strict.bas:2: warning: Using default implicit type 'float' for 'b' +strict.bas:4: strict mode: missing type declaration for 'a' +>>> process_file('errletfunc.bas') +errletfunc.bas:5: Cannot assign a value to 'x'. It's not a variable +>>> process_file('read0.bas') +read0.bas:12: 'x' is SUBROUTINE not a FUNCTION +>>> process_file('read1.bas') +read1.bas:11: Variable 'x' is an array and cannot be used in this context +>>> process_file('read3.bas') +read3.bas:9: 'x' is neither an array nor a function. +>>> process_file('read6.bas') +read6.bas:12: Syntax error. Can only read a variable or an array element +>>> process_file('data0.bas') +data0.bas:2: 'b' is neither an array nor a function. +>>> process_file('ifempty4.bas') +ifempty4.bas:3: warning: Useless empty IF ignored +>>> process_file('ifempty1.bas') +ifempty1.bas:3: warning: Useless empty IF ignored +>>> process_file('ifempty5.bas') +ifempty5.bas:3: warning: Condition is always True +>>> process_file('ifempty0.bas') +ifempty0.bas:3: warning: Useless empty IF ignored +>>> process_file('forempty.bas') +forempty.bas:4: warning: STEP value is 0 and FOR might loop forever +>>> process_file('fornextopt.bas') +fornextopt.bas:4: warning: FOR start value is greater than end. This FOR loop is useless +>>> process_file('fornextopt2.bas') +fornextopt2.bas:4: warning: FOR start value is lower than end. This FOR loop is useless +>>> process_file('atoloduplbl.asm') +atoloduplbl.asm:3: label '.SetSubScreen' already defined at line 2 +>>> process_file('asmerror2.asm') +asmerror2.asm:2: Error: illegal preprocessor character '@' +asmerror2.asm:2: Syntax error. Unexpected end of line [NEWLINE] +>>> process_file('llb.bas') +llb.bas:3: Undeclared function "f$" +>>> process_file('substr_expr_err.bas') +substr_expr_err.bas:3: Expected a string type expression. Got byte type instead +>>> process_file('dup_func_decl.bas') +dup_func_decl.bas:5: duplicated declaration for function 'f' +>>> process_file('def_func_inline.bas') +def_func_inline.bas:2: Syntax Error. Unexpected token 'END' +>>> process_file('let_array_substr4.bas') +let_array_substr4.bas:2: Array 'a' is not of type String +>>> process_file('let_array_substr6.bas') +let_array_substr6.bas:2: Array 'a' is not of type String +>>> process_file('let_array_substr8.bas') +let_array_substr8.bas:3: Array 'a' has 1 dimensions, not 2 +>>> process_file('let_array_wrong_dims.bas') +let_array_wrong_dims.bas:2: Array 'a' has 1 dimensions, not 2 +>>> process_file('alxinho1.bas') +alxinho1.bas:3: Undeclared array "a" +>>> process_file('func0.bas') +func0.bas:5: warning: Using default implicit type 'float' for 'f' +>>> process_file('bad_sigil.bas') +bad_sigil.bas:2: expected type string for 'y$', got float +bad_sigil.bas:2: warning: Parameter 'y' is never used +>>> process_file('params_implicit.bas') +params_implicit.bas:2: warning: Using default implicit type 'float' for 'y' +params_implicit.bas:2: warning: Parameter 'y' is never used +>>> process_file('array_err.bas') +array_err.bas:2: Mismatched vector size. Expected 11 elements, got 1. +>>> process_file('arrbase1.bas') + diff --git a/tests/functional/test_prepro.py b/tests/functional/test_prepro.py new file mode 100755 index 000000000..06d00d8bc --- /dev/null +++ b/tests/functional/test_prepro.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import test +import os +import sys +import pytest + + +TEST_PATH = os.path.dirname(os.path.realpath(__file__)) + + +@pytest.mark.parametrize('fname', [os.path.join(TEST_PATH, f) for f in os.listdir(TEST_PATH) if f.endswith(".bi")]) +def test_prepro(fname): + test.main(['-d', '-e', '/dev/null', fname]) + if test.COUNTER == 0: + return + sys.stderr.write("Total: %i, Failed: %i (%3.2f%%)\n" % + (test.COUNTER, test.FAILED, 100.0 * test.FAILED / float(test.COUNTER))) + + assert test.EXIT_CODE == 0, "Preprocessor test failed" diff --git a/tests/functional/tzx_00.tzx b/tests/functional/tzx_00.tzx index ec3f81df1..b0cf1539c 100644 Binary files a/tests/functional/tzx_00.tzx and b/tests/functional/tzx_00.tzx differ diff --git a/tests/functional/usr0.asm b/tests/functional/usr0.asm index 3ae5048d3..8096fec2a 100644 --- a/tests/functional/usr0.asm +++ b/tests/functional/usr0.asm @@ -39,15 +39,17 @@ __LABEL0: DEFW 0001h DEFB 41h #line 1 "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 "sposn.asm" + ; Printing positioning library. PROC - LOCAL ECHO_E - + LOCAL ECHO_E + __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem var. ld de, (S_POSN) ld hl, (MAXX) @@ -55,45 +57,46 @@ __LOAD_S_POSN: ; Loads into DE current ROW, COL print position from S_POSN mem sbc hl, de ex de, hl ret - - + + __SAVE_S_POSN: ; Saves ROW, COL from DE into S_POSN mem var. ld hl, (MAXX) or a sbc hl, de ld (S_POSN), hl ; saves it again ret - - + + ECHO_E EQU 23682 MAXX EQU ECHO_E ; Max X position + 1 MAXY EQU MAXX + 1 ; Max Y position + 1 - - S_POSN EQU 23688 + + S_POSN EQU 23688 POSX EQU S_POSN ; Current POS X POSY EQU S_POSN + 1 ; Current POS Y - + ENDP - + #line 6 "print.asm" #line 1 "cls.asm" + ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen - + ;CLS EQU 0DAFh - + ; Our faster implementation - - - + + + CLS: PROC - + LOCAL COORDS LOCAL __CLS_SCR LOCAL ATTR_P LOCAL SCREEN - + ld hl, 0 ld (COORDS), hl ld hl, 1821h @@ -106,41 +109,43 @@ __CLS_SCR: inc de ld bc, 6144 ldir - + ; Now clear attributes - + ld a, (ATTR_P) ld (hl), a ld bc, 767 ldir ret - + COORDS EQU 23677 SCREEN EQU 16384 ; Default start of the screen (can be changed) ATTR_P EQU 23693 ;you can poke (SCREEN_SCRADDR) to change CLS, DRAW & PRINTing address - + SCREEN_ADDR EQU (__CLS_SCR + 1) ; Address used by print and other screen routines ; to get the start of the screen ENDP - + #line 7 "print.asm" #line 1 "in_screen.asm" - + + #line 1 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -148,12 +153,13 @@ __CLS_SCR: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -161,50 +167,51 @@ __ERROR: __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 #line 3 "in_screen.asm" - + __IN_SCREEN: ; Returns NO carry if current coords (D, E) ; are OUT of the screen limits (MAXX, MAXY) - + PROC LOCAL __IN_SCREEN_ERR - + ld hl, MAXX ld a, e cp (hl) jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - + ld a, d inc hl cp (hl) ;; jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range ;; ret 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 #line 8 "print.asm" #line 1 "table_jump.asm" - + + 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) @@ -213,15 +220,17 @@ JUMP_HL_PLUS_DE: ; Does JP (HL + DE) ex de, hl CALL_HL: jp (hl) - + #line 9 "print.asm" #line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register - + #line 1 "const.asm" + ; Global constants - + P_FLAG EQU 23697 FLAGS2 EQU 23681 ATTR_P EQU 23693 ; permanet ATTRIBUTES @@ -229,26 +238,26 @@ CALL_HL: CHARS EQU 23606 ; Pointer to ROM/RAM Charset UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars - + #line 5 "ink.asm" - + 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 @@ -262,44 +271,45 @@ __SET_INK2: 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 - + #line 10 "print.asm" #line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + PAPER: PROC LOCAL __SET_PAPER LOCAL __SET_PAPER2 - + ld de, ATTR_P - + __SET_PAPER: - cp 8 + 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 + and 7 ; # Remove rlca rlca rlca ; a *= 8 - + ld b, a ; Saves the color ld a, (de) and 0C7h ; Clears previous value @@ -310,27 +320,28 @@ __SET_PAPER2: 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 - + #line 11 "print.asm" #line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + FLASH: ld de, ATTR_P __SET_FLASH: ; Another entry. This will set the flash flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca ld b, a ; Saves the color ld a, (de) @@ -338,27 +349,28 @@ __SET_FLASH: or b ld (de), a ret - - + + ; Sets the FLASH flag passed in A register in the ATTR_T variable FLASH_TMP: ld de, ATTR_T jr __SET_FLASH - + #line 12 "print.asm" #line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently ; Parameter: Paper color in A register - - - + + + BRIGHT: ld de, ATTR_P - + __SET_BRIGHT: ; Another entry. This will set the bright flag at location pointer by DE and 1 ; # Convert to 0/1 - + rrca rrca ld b, a ; Saves the color @@ -367,81 +379,83 @@ __SET_BRIGHT: or b ld (de), a ret - - + + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable BRIGHT_TMP: ld de, ATTR_T jr __SET_BRIGHT - + #line 13 "print.asm" #line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register #line 1 "copy_attr.asm" - - -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - - - + + + +#line 4 "/src/zxb/trunk/library-asm/copy_attr.asm" + + + COPY_ATTR: ; Just copies current permanent attribs to temporal attribs - ; and sets print mode + ; 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 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 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - + or (hl) ; OVER 3 MODE + +#line 65 "/src/zxb/trunk/library-asm/copy_attr.asm" + __REFRESH_TMP: ld a, (hl) and 10101010b @@ -450,32 +464,32 @@ __REFRESH_TMP: or c ld (hl), a ret - + ENDP - + #line 4 "over.asm" - - + + 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 @@ -485,7 +499,7 @@ OVER_TMP: res 0, (hl) or (hl) ld (hl), a - + ld a, c ; Recovers previous value and 1 ld hl, P_FLAG @@ -493,19 +507,20 @@ OVER_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 14 "print.asm" #line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently ; Parameter: INVERSE flag in bit 0 of A register - - - + + + INVERSE: PROC - + and 1 ; # Convert to 0/1 add a, a; # Shift left 3 bits for permanent add a, a @@ -515,7 +530,7 @@ INVERSE: or (hl) ld (hl), a ret - + ; Sets INVERSE flag in P_FLAG temporarily INVERSE_TMP: and 1 @@ -526,18 +541,19 @@ INVERSE_TMP: or (hl) ld (hl), a jp __SET_ATTR_MODE - + ENDP - + #line 15 "print.asm" #line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently ; Parameter: BOLD flag in bit 0 of A register - - + + BOLD: PROC - + and 1 rlca rlca @@ -547,7 +563,7 @@ BOLD: or (hl) ld (hl), a ret - + ; Sets BOLD flag in P_FLAG temporarily BOLD_TMP: and 1 @@ -558,18 +574,19 @@ BOLD_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 16 "print.asm" #line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently ; Parameter: ITALIC flag in bit 0 of A register - - + + ITALIC: PROC - + and 1 rrca rrca @@ -579,7 +596,7 @@ ITALIC: or (hl) ld (hl), a ret - + ; Sets ITALIC flag in P_FLAG temporarily ITALIC_TMP: and 1 @@ -592,21 +609,22 @@ ITALIC_TMP: or (hl) ld (hl), a ret - + ENDP - + #line 17 "print.asm" - + #line 1 "attr.asm" + ; Attribute routines ; vim:ts=4:et:sw: - - - - - - - + + + + + + + __ATTR_ADDR: ; calc start address in DE (as (32 * d) + e) ; Contributed by Santiago Romero at http://www.speccy.org @@ -615,79 +633,79 @@ __ATTR_ADDR: 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 - + ld d, 18h ; DE = 6144 + E. Note: 6144 is the screen size (before attr zone) add hl, de - + ld de, (SCREEN_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 - + __SET_ATTR: ; Internal __FASTCALL__ Entry used by printing routines - PROC - + PROC + call __ATTR_ADDR 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 - - + + #line 19 "print.asm" - - ; Putting a comment starting with @INIT
+ + ; Putting a comment starting with @INIT
; will make the compiler to add a CALL to
; It is useful for initialization routines. - - + + __PRINT_INIT: ; To be called before program starts (initializes library) PROC - + ld hl, __PRINT_START ld (PRINT_JUMP_STATE), hl - + ld hl, 1821h ld (MAXX), hl ; Sets current maxX and maxY - + xor a ld (FLAGS2), a - + ret - - + + __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_CONT LOCAL __PRINT_CONT2 @@ -696,75 +714,75 @@ __PRINTCHAR: ; Print character store in accumulator (A register) LOCAL __PRINT_UDG LOCAL __PRGRAPH LOCAL __PRINT_START - + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - + __PRINT_JUMP: jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - + __PRINT_START: cp ' ' jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - + exx ; Switch to alternative registers ex af, af' ; Saves a value (char to print) for later - + call __LOAD_S_POSN - + ; At this point we have the new coord ld hl, (SCREEN_ADDR) - + 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 add hl, de ; HL = Screen address + DE ex de, hl ; DE = Screen address - + ex af, af' - - cp 80h ; Is it an UDG or a ? + + cp 80h ; Is it an UDG or a ? jp c, __SRCADDR - + cp 90h jp nc, __PRINT_UDG - + ; Print a 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) jp __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 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) @@ -774,7 +792,7 @@ __PRGRAPH: ld b, 8 ; 8 bytes per char __PRCHAR: ld a, (de) ; DE *must* be ALWAYS source, and HL destiny - + PRINT_MODE: ; Which operation is used to write on the screen ; Set it with: ; LD A, @@ -786,16 +804,16 @@ PRINT_MODE: ; Which operation is used to write on the screen ; OR : B6h --> OR (HL) ; PUTSPRITE ; AND : A6h --> AND (HL) ; PUTMASK nop ; - + INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 nop ; 2F -> CPL -> INVERSE 1 - + ld (hl), a - - inc de + + inc de inc h ; Next line - djnz __PRCHAR - + djnz __PRCHAR + call __LOAD_S_POSN push de call __SET_ATTR @@ -809,49 +827,49 @@ INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 call __PRINT_EOL1 exx ; counteracts __PRINT_EOL1 exx jp __PRINT_CONT2 - + __PRINT_CONT: call __SAVE_S_POSN - + __PRINT_CONT2: exx ret - + ; ------------- SPECIAL CHARS (< 32) ----------------- - + __PRINT_SPECIAL: ; Jumps here if it is a special char exx 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) call __LOAD_S_POSN - + __PRINT_EOL1: ; Another entry called from PRINT when next line required ld e, 0 - + __PRINT_EOL2: ld a, d inc a - + __PRINT_AT1_END: ld hl, (MAXY) cp l jr c, __PRINT_EOL_END ; Carry if (MAXY) < d xor a - + __PRINT_EOL_END: - ld d, a - + ld d, a + __PRINT_AT2_END: call __SAVE_S_POSN exx ret - + __PRINT_COM: exx push hl @@ -862,17 +880,17 @@ __PRINT_COM: pop de pop hl ret - + __PRINT_TAB: ld hl, __PRINT_TAB1 jp __PRINT_SET_STATE - + __PRINT_TAB1: - ld (MEM0), a + ld (MEM0), a ld hl, __PRINT_TAB2 ld (PRINT_JUMP_STATE), hl ret - + __PRINT_TAB2: ld a, (MEM0) ; Load tab code (ignore the current one) push hl @@ -885,27 +903,27 @@ __PRINT_TAB2: pop de pop hl ret - + __PRINT_NOP: __PRINT_RESTART: ld hl, __PRINT_START jp __PRINT_SET_STATE - + __PRINT_AT: ld hl, __PRINT_AT1 - + __PRINT_SET_STATE: ld (PRINT_JUMP_STATE), hl ; Saves next entry call exx ret - + __PRINT_AT1: ; Jumps here if waiting for 1st parameter exx ld hl, __PRINT_AT2 ld (PRINT_JUMP_STATE), hl ; Saves next entry call call __LOAD_S_POSN jp __PRINT_AT1_END - + __PRINT_AT2: exx ld hl, __PRINT_START @@ -913,10 +931,10 @@ __PRINT_AT2: call __LOAD_S_POSN ld e, a ld hl, (MAXX) - cp (hl) + cp (hl) jr c, __PRINT_AT2_END jr __PRINT_EOL1 - + __PRINT_DEL: call __LOAD_S_POSN ; Gets current screen position dec e @@ -933,80 +951,80 @@ __PRINT_DEL: ld d, h dec d jp __PRINT_AT2_END - + __PRINT_INK: ld hl, __PRINT_INK2 jp __PRINT_SET_STATE - + __PRINT_INK2: exx call INK_TMP jp __PRINT_RESTART - + __PRINT_PAP: ld hl, __PRINT_PAP2 jp __PRINT_SET_STATE - + __PRINT_PAP2: exx call PAPER_TMP jp __PRINT_RESTART - + __PRINT_FLA: ld hl, __PRINT_FLA2 jp __PRINT_SET_STATE - + __PRINT_FLA2: exx call FLASH_TMP jp __PRINT_RESTART - + __PRINT_BRI: ld hl, __PRINT_BRI2 jp __PRINT_SET_STATE - + __PRINT_BRI2: exx call BRIGHT_TMP jp __PRINT_RESTART - + __PRINT_INV: ld hl, __PRINT_INV2 jp __PRINT_SET_STATE - + __PRINT_INV2: exx call INVERSE_TMP jp __PRINT_RESTART - + __PRINT_OVR: ld hl, __PRINT_OVR2 jp __PRINT_SET_STATE - + __PRINT_OVR2: exx call OVER_TMP jp __PRINT_RESTART - + __PRINT_BOLD: ld hl, __PRINT_BOLD2 jp __PRINT_SET_STATE - + __PRINT_BOLD2: exx call BOLD_TMP jp __PRINT_RESTART - + __PRINT_ITA: ld hl, __PRINT_ITA2 jp __PRINT_SET_STATE - + __PRINT_ITA2: exx call ITALIC_TMP jp __PRINT_RESTART - - + + __BOLD: push hl ld hl, MEM0 @@ -1023,8 +1041,8 @@ __BOLD_LOOP: pop hl ld de, MEM0 ret - - + + __ITALIC: push hl ld hl, MEM0 @@ -1048,17 +1066,17 @@ __ITALIC: pop hl ld de, MEM0 ret - + PRINT_COMMA: call __LOAD_S_POSN ld a, e and 16 add a, 16 - + PRINT_TAB: PROC LOCAL LOOP, CONTINUE - + inc a call __LOAD_S_POSN ; e = current row ld d, a @@ -1069,7 +1087,7 @@ PRINT_TAB: CONTINUE: ld a, d inc e - sub e ; A = A - E + sub e ; A = A - E and 31 ; ret z ; Already at position E ld b, a @@ -1079,21 +1097,21 @@ LOOP: djnz LOOP ret ENDP - + PRINT_AT: ; CHanges cursor to ROW, COL ; COL in A register - ; ROW in stack - + ; 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 __BOLD LOCAL __BOLD_LOOP @@ -1112,9 +1130,9 @@ PRINT_AT: ; CHanges cursor to ROW, COL LOCAL __PRINT_SET_STATE LOCAL __PRINT_TABLE LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - + __PRINT_TABLE: ; Jump table for 0 .. 22 codes - + DW __PRINT_NOP ; 0 DW __PRINT_NOP ; 1 DW __PRINT_NOP ; 2 @@ -1139,30 +1157,33 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes DW __PRINT_OVR ; 21 DW __PRINT_AT ; 22 AT DW __PRINT_TAB ; 23 TAB - + ENDP - - + + #line 26 "usr0.bas" #line 1 "printu16.asm" + #line 1 "printi16.asm" + #line 1 "printnum.asm" - - - + + + + __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 @@ -1170,28 +1191,30 @@ __PRINTU_CONT: pop bc djnz __PRINTU_CONT ret - + ENDP - - + + __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER mus preserve registers ld a, '-' jp __PRINT_DIGIT - + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - + + #line 2 "printi16.asm" #line 1 "div16.asm" - ; 16 bit division and modulo functions + + ; 16 bit division and modulo functions ; for both signed and unsigned values - + #line 1 "neg16.asm" + ; Negates HL value (16 bit) __ABS16: bit 7, h ret z - + __NEGHL: ld a, l ; HL = -HL cpl @@ -1201,23 +1224,23 @@ __NEGHL: ld h, a inc hl ret - + #line 5 "div16.asm" - + __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 @@ -1226,46 +1249,46 @@ __DIV16LOOP: 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 - + jr z, __DIVI16A + ld a, e ; DE = -DE cpl ld e, a @@ -1273,88 +1296,90 @@ __DIVI16_FAST: 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 + + 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 - + #line 3 "printi16.asm" - - - + + + __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 - + #line 2 "printu16.asm" - + #line 27 "usr0.bas" #line 1 "usr_str.asm" + ; 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) - - - + + + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1362,25 +1387,25 @@ __PRINTU_LOOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1389,40 +1414,41 @@ __PRINTU_LOOP: ; | (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 + ; 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 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -1430,25 +1456,25 @@ __PRINTU_LOOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -1457,39 +1483,39 @@ __PRINTU_LOOP: ; | (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 + ; 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 @@ -1497,56 +1523,56 @@ __PRINTU_LOOP: __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 + ; __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 +__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 - + #line 69 "free.asm" - + ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -1555,57 +1581,57 @@ __MEM_INIT2: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -1614,47 +1640,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -1664,27 +1690,27 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 11 "usr_str.asm" - + USR_STR: 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 11011111b ; Convert it to UPPER CASE @@ -1696,31 +1722,31 @@ USR_STR: 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 - + #line 28 "usr0.bas" - + ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: ; Defines DATA END diff --git a/tests/functional/utf-8-bom-asm.asm b/tests/functional/utf-8-bom-asm.asm new file mode 100644 index 000000000..a4c59fe8a --- /dev/null +++ b/tests/functional/utf-8-bom-asm.asm @@ -0,0 +1,4 @@ +datosniveles: +nop +; This file contains an UTF-8 BOM and must be correctly parsed + diff --git a/tests/functional/utf-8-bom-asm.bin b/tests/functional/utf-8-bom-asm.bin new file mode 100644 index 000000000..f76dd238a Binary files /dev/null and b/tests/functional/utf-8-bom-asm.bin differ diff --git a/tests/functional/utf-8-bom-bas.asm b/tests/functional/utf-8-bom-bas.asm new file mode 100644 index 000000000..9c82cd247 --- /dev/null +++ b/tests/functional/utf-8-bom-bas.asm @@ -0,0 +1,36 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL__datosniveles: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/utf-8-bom-bas.bas b/tests/functional/utf-8-bom-bas.bas new file mode 100644 index 000000000..b0913caad --- /dev/null +++ b/tests/functional/utf-8-bom-bas.bas @@ -0,0 +1,3 @@ +datosniveles: + +REM this file contains an UTF-8 BOM at the beginning diff --git a/tests/functional/valcrash2.asm b/tests/functional/valcrash2.asm index 7df6a11dd..24b51befd 100644 --- a/tests/functional/valcrash2.asm +++ b/tests/functional/valcrash2.asm @@ -47,15 +47,17 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "inkey.asm" + ; INKEY Function ; Returns a string allocated in dynamic memory ; containing the string. ; An empty string otherwise. - + #line 1 "alloc.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -63,25 +65,25 @@ __CALL_BACK__: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -90,50 +92,51 @@ __CALL_BACK__: ; | (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 + ; 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 "error.asm" + ; Simple error control routines ; vim:ts=4:et: - + 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 @@ -141,12 +144,13 @@ __CALL_BACK__: ERROR_NumberTooBig EQU 5 ERROR_InvalidArg EQU 9 ERROR_IntOutOfRange EQU 10 - ERROR_InvalidFileName EQU 14 + 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 @@ -154,7 +158,7 @@ __ERROR: __ERROR_CODE: nop ret - + ; Sets the error system variable, but keeps running. ; Usually this instruction if followed by the END intermediate instruction. __STOP: @@ -162,9 +166,10 @@ __STOP: ret #line 69 "alloc.asm" #line 1 "heapinit.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -172,25 +177,25 @@ __STOP: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -199,39 +204,39 @@ __STOP: ; | (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 + ; 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 @@ -239,57 +244,57 @@ __STOP: __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 + ; __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 +__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 - + #line 70 "alloc.asm" - - + + ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -301,39 +306,39 @@ __MEM_INIT2: ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) ; if the block could not be allocated (out of memory) ; --------------------------------------------------------------------- - + 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 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 111 "/src/zxb/trunk/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" +#line 113 "/src/zxb/trunk/library-asm/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 @@ -341,15 +346,15 @@ __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE 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. + +__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 @@ -374,14 +379,14 @@ __MEM_DONE: ; A free block has been found. 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 @@ -389,50 +394,50 @@ __MEM_SUBTRACT: 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 - - + + #line 7 "inkey.asm" - + INKEY: - PROC + PROC LOCAL __EMPTY_INKEY LOCAL KEY_SCAN LOCAL KEY_TEST LOCAL KEY_CODE - - ld bc, 3 ; 1 char length string + + 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 @@ -441,7 +446,7 @@ INKEY: dec hl dec hl ; HL Points to string result ret - + __EMPTY_INKEY: pop hl xor a @@ -450,15 +455,16 @@ __EMPTY_INKEY: ld (hl), a dec hl ret - + KEY_SCAN EQU 028Eh KEY_TEST EQU 031Eh KEY_CODE EQU 0333h - + ENDP - + #line 35 "valcrash2.bas" #line 1 "storef.asm" + __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 @@ -466,7 +472,7 @@ __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory 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) @@ -474,7 +480,7 @@ __ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address 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 @@ -486,103 +492,105 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL inc hl ld (hl), b ret - + #line 36 "valcrash2.bas" #line 1 "strcat.asm" - + + #line 1 "strlen.asm" + ; Returns len if a string ; If a string is NULL, its len is also 0 ; Result returned in HL - + __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 - - + + #line 3 "strcat.asm" - + __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$ - + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - + ex (sp), hl ; HL = a$, (SP) = b$ + exx - pop de ; D'E' = b$ + 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 ; + 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 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$ - + + 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$) @@ -590,54 +598,56 @@ __STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ 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 - + #line 37 "valcrash2.bas" #line 1 "val.asm" + #line 1 "free.asm" + ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa - ; (a.k.a. Boriel) + ; (a.k.a. Boriel) ; http://www.boriel.com ; ; This ASM library is licensed under the BSD license @@ -645,25 +655,25 @@ __STRCATEND: ; 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 + ; + ; +----------------+ <-- 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) |---+ @@ -672,38 +682,38 @@ __STRCATEND: ; | (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 + ; 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 @@ -712,57 +722,57 @@ __STRCATEND: ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing ; is done ; --------------------------------------------------------------------- - + 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) @@ -771,47 +781,47 @@ __MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL 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 + 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 @@ -821,42 +831,43 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed inc hl ld (hl), d ; Next saved ret - + ENDP - + #line 2 "val.asm" #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -872,15 +883,15 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 3 "val.asm" - - + + 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 @@ -890,107 +901,107 @@ VAL: ; Computes VAL(a$) using ROM FP-CALC 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 - + 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 hl, 0 add hl, sp ld (ERR_SP), hl - + 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_Ok ld (ERR_NR), a - + xor a ld b, a ld c, a ld d, b ld e, c ret - + ENDP - + #line 38 "valcrash2.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/while.asm b/tests/functional/while.asm index 063426459..e7a34d56a 100644 --- a/tests/functional/while.asm +++ b/tests/functional/while.asm @@ -37,10 +37,6 @@ __LABEL__40: jp __LABEL2 __LABEL3: jp __LABEL__20 - jp __LABEL__40 - ld hl, 0 - ld b, h - ld c, l __END_PROGRAM: di ld hl, (__CALL_BACK__) @@ -55,38 +51,41 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "ltf.asm" + #line 1 "u32tofreg.asm" + #line 1 "neg32.asm" + __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 - + #line 2 "u32tofreg.asm" __I8TOFREG: ld l, a @@ -95,35 +94,35 @@ __I8TOFREG: 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 @@ -131,20 +130,20 @@ __U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) 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 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 ? @@ -152,45 +151,47 @@ __U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG 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 - + #line 2 "ltf.asm" #line 1 "ftou32reg.asm" - - + + + __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 + jr nz, __IS_FLOAT ; Here if it is a ZX ROM Integer - + ld h, c ld l, d ld a, e ; Takes sign: FF = -, 0 = + @@ -198,95 +199,112 @@ __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS inc a jp z, __NEG32 ; Negates if negative ret - + __IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e + 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 ; - + 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, __NEG32 ; Negates DEHL - + 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 - + #line 3 "ltf.asm" #line 1 "stackf.asm" + ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- - - + + __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 @@ -302,37 +320,38 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH #line 4 "ltf.asm" - + ; ------------------------------------------------------------- ; 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. ; ; Uses CALLEE convention ; ------------------------------------------------------------- - + __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 + + call __FPSTACK_POP jp __FTOU8 ; Convert to 8 bits - -#line 46 "while.bas" + +#line 42 "while.bas" #line 1 "pushf.asm" - - ; Routine to push Float pointed by HL + + + ; 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 - + __FP_PUSH_REV: push hl exx @@ -353,10 +372,10 @@ __FP_PUSH_REV: push bc ; Return Address exx ret - - -#line 47 "while.bas" - + + +#line 43 "while.bas" + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00, 00 diff --git a/tests/functional/whileempty.bas b/tests/functional/whileempty.bas new file mode 100644 index 000000000..13dddf25a --- /dev/null +++ b/tests/functional/whileempty.bas @@ -0,0 +1,3 @@ +DIM i as Byte +WHILE i < 5 END WHILE + diff --git a/tests/functional/whileempty1.asm b/tests/functional/whileempty1.asm new file mode 100644 index 000000000..0d985726a --- /dev/null +++ b/tests/functional/whileempty1.asm @@ -0,0 +1,71 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL0: + ld h, 5 + ld a, (_i) + call __LTI8 + or a + jp nz, __LABEL0 +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "lti8.asm" + +#line 1 "lei8.asm" + +__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 +#line 2 "lti8.asm" +#line 25 "whileempty1.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/whileempty1.bas b/tests/functional/whileempty1.bas new file mode 100644 index 000000000..eb345f0bd --- /dev/null +++ b/tests/functional/whileempty1.bas @@ -0,0 +1,4 @@ +DIM i as Byte +WHILE i < 5 +END WHILE + diff --git a/tests/functional/whilefalse.asm b/tests/functional/whilefalse.asm new file mode 100644 index 000000000..237c470ee --- /dev/null +++ b/tests/functional/whilefalse.asm @@ -0,0 +1,37 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/whilefalse.bas b/tests/functional/whilefalse.bas new file mode 100644 index 000000000..1c44967d0 --- /dev/null +++ b/tests/functional/whilefalse.bas @@ -0,0 +1,5 @@ +DIM a as Byte +WHILE 0 + a = a + 1 +WEND + diff --git a/tests/functional/whilefalse1.asm b/tests/functional/whilefalse1.asm new file mode 100644 index 000000000..9025396e5 --- /dev/null +++ b/tests/functional/whilefalse1.asm @@ -0,0 +1,42 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL0: + jp __LABEL1 +__LABEL__BAD: + ld hl, _a + inc (hl) + jp __LABEL0 +__LABEL1: + jp __LABEL__BAD +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/whilefalse1.bas b/tests/functional/whilefalse1.bas new file mode 100644 index 000000000..ddb6d63e8 --- /dev/null +++ b/tests/functional/whilefalse1.bas @@ -0,0 +1,7 @@ +DIM a as Byte +WHILE 0 +BAD: + a = a + 1 +WEND +GOTO BAD + diff --git a/tests/functional/whilesplitted.asm b/tests/functional/whilesplitted.asm new file mode 100644 index 000000000..701b8dfc0 --- /dev/null +++ b/tests/functional/whilesplitted.asm @@ -0,0 +1,391 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL0: + ld hl, _i + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __EQF + or a + jp z, __LABEL1 + xor a + ld (_M), a + jp __LABEL0 +__LABEL1: +__LABEL2: + ld hl, _i + 4 + call __FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call __EQF + or a + jp z, __LABEL3 + xor a + ld (_M), a + jp __LABEL2 +__LABEL3: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "eqf.asm" + +#line 1 "u32tofreg.asm" + +#line 1 "neg32.asm" + +__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 + +#line 2 "u32tofreg.asm" +__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 + +#line 2 "eqf.asm" +#line 1 "ftou32reg.asm" + + + +__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 a, e ; Takes sign: FF = -, 0 = + + ld de, 0 + inc a + jp z, __NEG32 ; Negates if negative + 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 + +#line 3 "eqf.asm" +#line 1 "stackf.asm" + + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + + + __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 +#line 4 "eqf.asm" + + ; ------------------------------------------------------------- + ; 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 + ; ------------------------------------------------------------- + + +__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 + +#line 44 "whilesplitted.bas" +#line 1 "pushf.asm" + + + ; 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 + +__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 + + +#line 45 "whilesplitted.bas" + +ZXBASIC_USER_DATA: +_i: + DEFB 00, 00, 00, 00, 00 +_M: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/whilesplitted.bas b/tests/functional/whilesplitted.bas new file mode 100644 index 000000000..1d39bb9f7 --- /dev/null +++ b/tests/functional/whilesplitted.bas @@ -0,0 +1,7 @@ + +WHILE i=1: +LET M=0: WEND + +WHILE i=1: +LET M=0: END WHILE + diff --git a/tests/functional/whiletrue.asm b/tests/functional/whiletrue.asm new file mode 100644 index 000000000..5484805be --- /dev/null +++ b/tests/functional/whiletrue.asm @@ -0,0 +1,42 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei +__LABEL0: + ld hl, _a + inc (hl) + jp __LABEL0 +__LABEL1: + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 + +ZXBASIC_USER_DATA: +_a: + DEFB 00 + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/whiletrue.bas b/tests/functional/whiletrue.bas new file mode 100644 index 000000000..da05157d9 --- /dev/null +++ b/tests/functional/whiletrue.bas @@ -0,0 +1,5 @@ +DIM a as Byte +WHILE 1 + a = a + 1 +WEND + diff --git a/tests/functional/xor16.asm b/tests/functional/xor16.asm index 636f13fff..6e1ac25fb 100644 --- a/tests/functional/xor16.asm +++ b/tests/functional/xor16.asm @@ -51,37 +51,39 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "xor16.asm" - + + ; XOR16 implemented in XOR8.ASM file #line 1 "xor8.asm" + ; 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 - + __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 - + ret + #line 4 "xor16.asm" #line 42 "xor16.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00 diff --git a/tests/functional/xor32.asm b/tests/functional/xor32.asm index 8722a149f..b82863de9 100644 --- a/tests/functional/xor32.asm +++ b/tests/functional/xor32.asm @@ -67,46 +67,48 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "xor32.asm" + ; 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 - + #line 1 "xor8.asm" + ; 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 - + __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 - + ret + #line 7 "xor32.asm" - + __XOR32: ld a, h or l or d or e ld c, a - + pop hl ; RET address pop de ex (sp), hl @@ -116,9 +118,9 @@ __XOR32: or e ld h, c jp __XOR8 - + #line 58 "xor32.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00, 00, 00, 00 diff --git a/tests/functional/xor8.asm b/tests/functional/xor8.asm index 31546c1d5..9b4518f13 100644 --- a/tests/functional/xor8.asm +++ b/tests/functional/xor8.asm @@ -43,33 +43,34 @@ __END_PROGRAM: __CALL_BACK__: DEFW 0 #line 1 "xor8.asm" + ; 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 - + __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 - + ret + #line 34 "xor8.bas" - + ZXBASIC_USER_DATA: _a: DEFB 00 diff --git a/tests/runtime/arrcheck2.bas b/tests/runtime/arrcheck2.bas new file mode 100644 index 000000000..3bb2b033b --- /dev/null +++ b/tests/runtime/arrcheck2.bas @@ -0,0 +1,24 @@ + +DIM a(7, 19, 11) as UInteger +DIM p = @a(0, 0, 0) +DIM i as Uinteger +DIM j as UInteger +DIM k as UInteger +DIM m as UInteger + +FOR i = 0 TO 7 + FOR j = 0 TO 19 + FOR k = 0 TO 11 + LET m = i * j * k + LET a(i, j, k) = m + PRINT AT 0, 0; i; " "; j; " "; k; " " + IF PEEK(UInteger, p) <> m THEN + PRINT INK 2; FLASH 1; " ERROR " + STOP + END IF + LET p = p + 2 + NEXT k + NEXT j +NEXT i + +PRINT INK 4; FLASH 1; " OK " diff --git a/tests/runtime/ongoto.bas b/tests/runtime/ongoto.bas new file mode 100644 index 000000000..fcbd0c9f0 --- /dev/null +++ b/tests/runtime/ongoto.bas @@ -0,0 +1,18 @@ + +DIM a as UBYte = 1 +ON a + 1 GOTO 10, 20, 30 + +10 PRINT "10" +20 PRINT "20" +30 PRINT "30" + +ON a + 2 GOSUB 40, 50, 60, 70 +ON 1 GOSUB 50, 60 +PRINT "END" + +END +40 PRINT "40": RETURN +50 PRINT "50": RETURN +60 PRINT "60": RETURN +70 PRINT "70": RETURN + diff --git a/tests/runtime/readokdown.bas b/tests/runtime/readokdown.bas new file mode 100644 index 000000000..fbfc548a4 --- /dev/null +++ b/tests/runtime/readokdown.bas @@ -0,0 +1,43 @@ +REM Type coerding (reduction) in read DATA + +DIM i8 as Byte +DIM u8 as UByte +DIM i16 as Integer +DIM u16 as UInteger +DIM i32 as Long +DIM u32 as ULong +DIM f16 as Fixed +DIM flt as Float + +RESTORE +READ i8 +PRINT i8 +RESTORE +READ u8 +PRINT u8 + +RESTORE +READ i16 +PRINT i16 +RESTORE +READ u16 +PRINT u16 + +RESTORE +READ i32 +PRINT i32 +RESTORE +READ u32 +PRINT u32 + +RESTORE +READ f16 +PRINT f16 + +RESTORE +READ flt +PRINT flt + +DATA -1.5 + + diff --git a/tests/runtime/readokup.bas b/tests/runtime/readokup.bas new file mode 100644 index 000000000..15efcded0 --- /dev/null +++ b/tests/runtime/readokup.bas @@ -0,0 +1,43 @@ +REM Type promoting in read DATA + +DIM i8 as Byte +DIM u8 as UByte +DIM i16 as Integer +DIM u16 as UInteger +DIM i32 as Long +DIM u32 as ULong +DIM f16 as Fixed +DIM flt as Float + +RESTORE +READ i8 +PRINT i8 +RESTORE +READ u8 +PRINT u8 + +RESTORE +READ i16 +PRINT i16 +RESTORE +READ u16 +PRINT u16 + +RESTORE +READ i32 +PRINT i32 +RESTORE +READ u32 +PRINT u32 + +RESTORE +READ f16 +PRINT f16 + +RESTORE +READ flt +PRINT flt + +DATA -1 + + diff --git a/tests/symbols/test_symbolARRAYACCESS.py b/tests/symbols/test_symbolARRAYACCESS.py index bd0759086..40b187f79 100644 --- a/tests/symbols/test_symbolARRAYACCESS.py +++ b/tests/symbols/test_symbolARRAYACCESS.py @@ -10,10 +10,12 @@ import api.config as config from api.symboltable import SymbolTable from symbols.type_ import Type +import zxbpp class TestSymbolARRAYACCESS(TestCase): def setUp(self): + zxbpp.init() l1 = 1 l2 = 2 l3 = 3 diff --git a/tests/symbols/test_symbolBINARY.py b/tests/symbols/test_symbolBINARY.py index 30d956f84..11a6ed208 100644 --- a/tests/symbols/test_symbolBINARY.py +++ b/tests/symbols/test_symbolBINARY.py @@ -7,10 +7,12 @@ from api.config import OPTIONS import symbols from symbols.type_ import Type +import zxbpp class TestSymbolBINARY(TestCase): def setUp(self): + zxbpp.init() self.l = symbols.VAR('a', lineno=1, type_=Type.ubyte) self.r = symbols.NUMBER(3, lineno=2) self.b = symbols.BINARY('PLUS', self.l, self.r, lineno=3) diff --git a/tests/symbols/test_symbolBLOCK.py b/tests/symbols/test_symbolBLOCK.py index 82a7d9858..004f1ae77 100644 --- a/tests/symbols/test_symbolBLOCK.py +++ b/tests/symbols/test_symbolBLOCK.py @@ -48,6 +48,12 @@ def test_make_node_optimize2(self): for x in b: self.assertIsInstance(x, NUMBER) + def test_make_node_optimize3(self): + n = NUMBER(1, lineno=1) + b = BLOCK.make_node(BLOCK(BLOCK(BLOCK())), BLOCK(BLOCK(n), BLOCK(BLOCK()))) + self.assertEqual(len(b), 1) + self.assertEqual(b, BLOCK(n)) + def test__eq__(self): b = BLOCK() self.assertEqual(b, b) diff --git a/tests/symbols/test_symbolBOUND.py b/tests/symbols/test_symbolBOUND.py index 518492bcc..a9978092b 100644 --- a/tests/symbols/test_symbolBOUND.py +++ b/tests/symbols/test_symbolBOUND.py @@ -7,9 +7,13 @@ from api.config import OPTIONS import symbols +import zxbpp class TestSymbolBOUND(TestCase): + def setUp(self): + zxbpp.init() + def test__init__(self): self.assertRaises(AssertionError, symbols.BOUND, 'a', 3) self.assertRaises(AssertionError, symbols.BOUND, 1, 'a') diff --git a/tests/symbols/test_symbolFUNCDECL.py b/tests/symbols/test_symbolFUNCDECL.py index 162850ac6..181b499aa 100644 --- a/tests/symbols/test_symbolFUNCDECL.py +++ b/tests/symbols/test_symbolFUNCDECL.py @@ -14,10 +14,10 @@ class TestSymbolFUNCDECL(TestCase): def setUp(self): api.global_.SYMBOL_TABLE = api.symboltable.SymbolTable() self.f = gl.SYMBOL_TABLE.declare_func('f', 1, type_=Type.ubyte) - self.s = FUNCDECL(self.f) + self.s = FUNCDECL(self.f, 1) def test__init__fail(self): - self.assertRaises(AssertionError, FUNCDECL, 'bla') + self.assertRaises(AssertionError, FUNCDECL, 'bla', 1) def test_entry__getter(self): self.assertEqual(self.s.entry, self.f) diff --git a/tests/symbols/test_symbolNOP.py b/tests/symbols/test_symbolNOP.py new file mode 100644 index 000000000..dd63ec8b7 --- /dev/null +++ b/tests/symbols/test_symbolNOP.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from unittest import TestCase +from symbols import NOP + + +__author__ = 'boriel' + + +class TestSymbolBLOCK(TestCase): + def setUp(self): + self.nop = NOP() + + def test__len_0(self): + self.assertEqual(len(self.nop), 0, "NOP must have 0 length") + + def test__assert_false(self): + self.assertFalse(self.nop) diff --git a/tests/symbols/test_symbolNUMBER.py b/tests/symbols/test_symbolNUMBER.py index 2cf67097c..c0f228d01 100644 --- a/tests/symbols/test_symbolNUMBER.py +++ b/tests/symbols/test_symbolNUMBER.py @@ -7,6 +7,7 @@ from api.constants import TYPE from symbols import NUMBER from symbols import BASICTYPE +from symbols import CONST class TestSymbolNUMBER(TestCase): @@ -41,10 +42,162 @@ def test__cmp__(self): self.assertGreater(m, n) self.assertLess(n, m) + def test__cmp__const(self): + n = NUMBER(0, lineno=1) + m = CONST(NUMBER(1, lineno=2), lineno=2) + m2 = CONST(NUMBER(0, lineno=3), lineno=3) + + self.assertNotEqual(n, m) + self.assertEqual(n, n) + + self.assertNotEqual(n, 2) + self.assertEqual(n, 0) + self.assertGreater(n, -1) + self.assertLess(n, 1) + + self.assertGreater(m, n) + self.assertLess(n, m) + self.assertEqual(n, m2) + self.assertEqual(m2, n) + def test__t(self): n = NUMBER(3.14, 1) self.assertEqual(n.t, '3.14') + def test__add__num_num(self): + a = NUMBER(1, 0) + b = NUMBER(2, 0) + self.assertEqual((a + b).t, '3') + + def test__add__num_const(self): + a = NUMBER(1, 0) + b = CONST(NUMBER(2, 0), 0) + self.assertEqual((a + b).t, '3') + + def test__add__num_value(self): + a = NUMBER(1, 0) + self.assertEqual((a + 2).t, '3') + + def test__radd__num_const(self): + a = NUMBER(1, 0) + b = CONST(NUMBER(2, 0), 0) + self.assertEqual((b + a).t, '3') + + def test__radd__num_value(self): + a = NUMBER(1, 0) + self.assertEqual((2 + a).t, '3') + + def test__sub__num_num(self): + a = NUMBER(1, 0) + b = NUMBER(2, 0) + self.assertEqual((a - b).t, '-1') + + def test__sub__num_const(self): + a = NUMBER(1, 0) + b = CONST(NUMBER(2, 0), 0) + self.assertEqual((a - b).t, '-1') + + def test__sub__num_value(self): + a = NUMBER(1, 0) + self.assertEqual((a - 2).t, '-1') + + def test__rsub__num_const(self): + a = NUMBER(2, 0) + b = CONST(NUMBER(1, 0), 0) + self.assertEqual((b - a).t, '-1') + + def test__rsub__num_value(self): + a = NUMBER(2, 0) + self.assertEqual((1 - a).t, '-1') + + def test__mul__num_num(self): + a = NUMBER(3, 0) + b = NUMBER(2, 0) + self.assertEqual((a * b).t, '6') + + def test__mul__num_const(self): + a = NUMBER(3, 0) + b = CONST(NUMBER(2, 0), 0) + self.assertEqual((a * b).t, '6') + + def test__mul__num_value(self): + a = NUMBER(3, 0) + self.assertEqual((a * 2).t, '6') + + def test__rmul__num_const(self): + a = NUMBER(3, 0) + b = CONST(NUMBER(2, 0), 0) + self.assertEqual((b * a).t, '6') + + def test__rmul__num_value(self): + a = NUMBER(3, 0) + self.assertEqual((2 * a).t, '6') + + def test__div__num_num(self): + a = NUMBER(3, 0) + b = NUMBER(-2, 0) + self.assertEqual((a / b).t, str(a.value / b.value)) + + def test__div__num_const(self): + a = NUMBER(3, 0) + b = CONST(NUMBER(-2, 0), 0) + self.assertEqual((a / b).t, str(a.value / b.expr.value)) + + def test__div__num_value(self): + a = NUMBER(3, 0) + self.assertEqual((a / -2.0).t, '-1.5') + + def test__rdiv__num_const(self): + a = CONST(NUMBER(-3, 0), 0) + b = NUMBER(2, 0) + self.assertEqual((a / b).t, str(a.expr.value / b.value)) + + def test__rdiv__num_value(self): + a = NUMBER(-2, 0) + self.assertEqual((3.0 / a).t, '-1.5') + + def test__bor__val_num(self): + b = NUMBER(5, 0) + self.assertEqual((3 | b).t, '7') + + def test__bor__num_val(self): + b = NUMBER(5, 0) + self.assertEqual((b | 3).t, '7') + + def test__band__num_val(self): + b = NUMBER(5, 0) + self.assertEqual((b & 3).t, '1') + + def test__band__val_num(self): + b = NUMBER(5, 0) + self.assertEqual((3 & b).t, '1') + + def test__mod__num_val(self): + b = NUMBER(5, 0) + self.assertEqual((b % 3).t, '2') + + def test__mod__val_num(self): + b = NUMBER(3, 0) + self.assertEqual((5 % b).t, '2') + + def test__le__val_num(self): + b = NUMBER(3, 0) + self.assertLessEqual(2, b) + + def test__le__num_num(self): + a = NUMBER(2, 0) + b = NUMBER(3, 0) + self.assertLessEqual(a, b) + + def test__ge__val_num(self): + b = NUMBER(1, 0) + self.assertGreaterEqual(2, b) + + def test__ge__num_num(self): + a = NUMBER(4, 0) + b = NUMBER(3, 0) + self.assertGreaterEqual(a, b) + if __name__ == '__main__': unittest.main() diff --git a/tests/symbols/test_symbolSTRSLICE.py b/tests/symbols/test_symbolSTRSLICE.py index c95fdec49..608038f08 100644 --- a/tests/symbols/test_symbolSTRSLICE.py +++ b/tests/symbols/test_symbolSTRSLICE.py @@ -55,6 +55,11 @@ def test_make_node(self): self.assertIsInstance(s, symbols.STRING) self.assertEqual(s.value, 'XB') + def test_make_node_wrong(self): + bad_index = symbols.VAR('a', 0, type_=gl.SYMBOL_TABLE.basic_types[gl.TYPE.string]) + s = symbols.STRSLICE.make_node(1, self.str_, bad_index, bad_index) + self.assertIsNone(s) + if __name__ == '__main__': unittest.main() diff --git a/tests/symbols/test_symbolTYPECAST.py b/tests/symbols/test_symbolTYPECAST.py index 1b90a92f3..b1c2ce43c 100644 --- a/tests/symbols/test_symbolTYPECAST.py +++ b/tests/symbols/test_symbolTYPECAST.py @@ -9,12 +9,15 @@ from api.config import OPTIONS from six import StringIO from api.constants import CLASS +import zxbpp + __autor__ = 'boriel' class TestSymbolTYPECAST(TestCase): def setUp(self): + zxbpp.init() self.t = TYPECAST(Type.float_, NUMBER(3, lineno=1), lineno=2) if OPTIONS.has_option('stderr'): @@ -44,8 +47,8 @@ def test_make_node(self): self.assertEqual(t, self.t.operand) def test_make_const(self): - ''' Must return a number - ''' + """ Must return a number + """ v = VAR('a', lineno=1, type_=Type.byte_) v.default_value = 3 v.class_ = CLASS.const @@ -54,8 +57,8 @@ def test_make_const(self): self.assertEqual(t, 3) def test_make_node_None(self): - ''' None is allowed as operand - ''' + """ None is allowed as operand + """ self.assertIsNone(TYPECAST.make_node(Type.float_, None, lineno=2)) def test_make_node_fail_type(self): diff --git a/tox.ini b/tox.ini index 6ecda7347..25707ddd7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,18 +1,22 @@ [tox] -envlist = py27,py35,pypy,flake8 +envlist = py27,py36,pypy,flake8 [testenv] setenv = LANG=en_US.UTF-8 deps = pytest + pytest-cov + pytest-timeout -rrequirements.txt commands = - py.test - /bin/bash -c 'cd ./tests/functional && make' + py.test \ + --cov-report html:htmlcov/{envname} \ + --cov . \ + {posargs} [flake8] max-line-length = 120 -ignore = E731 +ignore = E722, E731, E741, W504 exclude = .cache/, .tox/, @@ -29,4 +33,7 @@ commands = flake8 [travis] python = - 3.5: py35, flake8 + 3.6: py36, flake8 + +[pytest] +norecursedirs = test_*tmp diff --git a/version.py b/version.py index d50440bd9..051375da4 100755 --- a/version.py +++ b/version.py @@ -1 +1 @@ -VERSION = '1.6.9' +VERSION = '1.8.9' diff --git a/zxb.py b/zxb.py index 06ac67694..f57809685 100755 --- a/zxb.py +++ b/zxb.py @@ -50,7 +50,8 @@ def output(memory, ofile=None): and writes it to the given file or to the screen if no file is passed """ for m in memory: - if len(m) > 0 and m[0] == '#': # Preprocessor directive? + m = m.rstrip('\r\n\t ') # Ensures no trailing newlines (might with upon includes) + if m and m[0] == '#': # Preprocessor directive? if ofile is None: print(m) else: @@ -58,7 +59,7 @@ def output(memory, ofile=None): continue # Prints a 4 spaces "tab" for non labels - if ':' not in m: + if m and ':' not in m: if ofile is None: print(' '), else: @@ -105,7 +106,7 @@ def main(args=None): help="Sets the program to be run once loaded") parser.add_argument('-A', '--asm', action='store_true', help="Sets output format to asm") - parser.add_argument('-S', '--org', type=int, default=OPTIONS.org.value, + parser.add_argument('-S', '--org', type=str, default=str(OPTIONS.org.value), help="Start of machine code. By default %i" % OPTIONS.org.value) parser.add_argument('-e', '--errmsg', type=str, dest='stderr', default=OPTIONS.StdErrFileName.value, help='Error messages file (standard error console by default)') @@ -138,7 +139,13 @@ def main(args=None): help='Ignore case. Makes variable names are case insensitive') parser.add_argument('-I', '--include-path', type=str, default='', help='Add colon separated list of directories to add to include path. e.g. -I dir1:dir2') + parser.add_argument('--strict', action='store_true', + help='Enables strict mode. Force explicit type declaration') + parser.add_argument('--headerless', action='store_true', + help='Header-less mode: omit asm prologue and epilogue') parser.add_argument('--version', action='version', version='%(prog)s {0}'.format(VERSION)) + parser.add_argument('--parse-only', action='store_true', + help="Only parses to check for syntax and semantic errors") options = parser.parse_args(args=args) @@ -153,7 +160,6 @@ def main(args=None): OPTIONS.array_base.value = options.array_base OPTIONS.string_base.value = options.string_base OPTIONS.Sinclair.value = options.sinclair - OPTIONS.org.value = options.org OPTIONS.heap_size.value = options.heap_size OPTIONS.memoryCheck.value = options.debug_memory OPTIONS.strictBool.value = options.strict_bool or OPTIONS.Sinclair.value @@ -162,6 +168,12 @@ def main(args=None): OPTIONS.enableBreak.value = options.enable_break OPTIONS.explicit.value = options.explicit OPTIONS.memory_map.value = options.memory_map + OPTIONS.strict.value = options.strict + OPTIONS.headerless.value = options.headerless + + OPTIONS.org.value = api.utils.parse_int(options.org) + if OPTIONS.org.value is None: + parser.error("Invalid --org option '{}'".format(options.org)) if options.defines: for i in options.defines: @@ -180,8 +192,9 @@ def main(args=None): debug.ENABLED = OPTIONS.Debug.value - if int(options.tzx) + int(options.tap) + int(options.asm) + int(options.emit_backend) > 1: - parser.error("Options --tap, --tzx, --emit-backend and --asm are mutually exclusive") + if int(options.tzx) + int(options.tap) + int(options.asm) + int(options.emit_backend) + \ + int(options.parse_only) > 1: + parser.error("Options --tap, --tzx, --emit-backend, --parse-only and --asm are mutually exclusive") return 3 if options.basic and not options.tzx and not options.tap: @@ -214,10 +227,6 @@ def main(args=None): zxbpp.ID_TABLE.define('__CHECK_ARRAY_BOUNDARY__', lineno=0) OPTIONS.include_path.value = options.include_path - - zxbpp.setMode('basic') - zxbpp.main(args) - input_ = zxbpp.OUTPUT OPTIONS.inputFileName.value = zxbparser.FILENAME = \ os.path.basename(args[0]) @@ -229,9 +238,16 @@ def main(args=None): if OPTIONS.StdErrFileName.value: OPTIONS.stderr.value = open_file(OPTIONS.StdErrFileName.value, 'wt', 'utf-8') + zxbpp.setMode('basic') + zxbpp.main(args) + + if gl.has_errors: + debug.__DEBUG__("exiting due to errors.") + return 1 # Exit with errors + + input_ = zxbpp.OUTPUT zxbparser.parser.parse(input_, lexer=zxblex.lexer, tracking=True, debug=(OPTIONS.Debug.value > 2)) - if gl.has_errors: debug.__DEBUG__("exiting due to errors.") return 1 # Exit with errors @@ -244,12 +260,19 @@ def main(args=None): translator = arch.zx48k.Translator() translator.visit(zxbparser.ast) + if gl.DATA_IS_USED: + gl.FUNCTIONS.extend(gl.DATA_FUNCTIONS) + # This will fill MEMORY with pending functions func_visitor = arch.zx48k.FunctionTranslator(gl.FUNCTIONS) func_visitor.start() + # Emits data lines + translator.emit_data_blocks() # Emits default constant strings translator.emit_strings() + # Emits jump tables + translator.emit_jump_tables() if OPTIONS.emitBackend.value: with open_file(OPTIONS.outputFileName.value, 'wt', 'utf-8') as output_file: @@ -302,7 +325,7 @@ def main(args=None): if options.asm: # Only output assembler file with open_file(OPTIONS.outputFileName.value, 'wt', 'utf-8') as output_file: output(asm_output, output_file) - else: + elif not options.parse_only: fout = StringIO() output(asm_output, fout) asmparse.assemble(fout.getvalue()) diff --git a/zxbasic_logo.png b/zxbasic_logo.png new file mode 100644 index 000000000..1064b4488 Binary files /dev/null and b/zxbasic_logo.png differ diff --git a/zxbasm.py b/zxbasm.py index 319b6539e..be4a999de 100755 --- a/zxbasm.py +++ b/zxbasm.py @@ -19,15 +19,17 @@ from asmparse import Asm, Expr, Container import zxbpp +import api.config from api.config import OPTIONS from api import global_ # Release version -VERSION = '1.10' +VERSION = '1.11' def main(args=None): # Initializes asm parser state + api.config.init() asmparse.init() zxbpp.init() diff --git a/zxbasmpplex.py b/zxbasmpplex.py index b3f4fd5e0..f97bc9dd3 100755 --- a/zxbasmpplex.py +++ b/zxbasmpplex.py @@ -13,8 +13,8 @@ import os import sys from ply import lex -from api.config import OPTIONS import api.utils +from prepro.output import warning, error EOL = '\n' @@ -105,7 +105,7 @@ def t_INITIAL_CHAR(self, t): return t def t_INITIAL_TOKEN(self, t): - r"[][%',.:$()*/<>~&|+-]" + r"[][%',.:$()*/<>~&|+^-]" return t def t_prepro_define_defargs_defargsopt_defexpr_pragma_NEWLINE(self, t): @@ -151,8 +151,7 @@ def t_prepro_ID(self, t): t.type = reserved_directives.get(t.value.lower(), 'ID') if t.type == 'DEFINE': t.lexer.begin('define') - - if t.type == 'PRAGMA': + elif t.type == 'PRAGMA': t.lexer.begin('pragma') return t @@ -284,10 +283,14 @@ def t_prepro_FILENAME(self, t): t.value = t.value[1:-1] # Remove quotes return t + def t_defargs_defargsopt_prepro_define_defexpr_pragma_singlecomment_INITIAL_asmcomment_ANY(self, t): + r'.' + self.error("illegal preprocessor character '%s'" % t.value[0]) + def t_defargs_defargsopt_prepro_define_defexpr_pragma_singlecomment_INITIAL_asmcomment_error(self, t): - """ error handling rule + """ error handling rule. This should never happens! """ - self.error("illegal preprocessor character '%s'" % t.value[0]) + pass # The lexer will raise an exception here. This is intended def put_current_line(self, prefix=''): """ Returns line and file for include / end of include sequences. @@ -298,7 +301,7 @@ def include(self, filename): """ Changes FILENAME and line count """ if filename != STDIN and filename in [x[0] for x in self.filestack]: # Already included? - self.warning(filename + ' Recursive inclusion') + self.warning(' Recursive inclusion') self.filestack.append([filename, 1, self.lex, self.input_data]) self.lex = lex.lex(object=self) @@ -312,11 +315,10 @@ def include(self, filename): if len(self.input_data) and self.input_data[-1] != EOL: self.input_data += EOL - - self.lex.input(self.input_data) except IOError: - self.error('cannot open "%s" file' % filename) + self.input_data = EOL + self.lex.input(self.input_data) return result def include_end(self): @@ -326,7 +328,7 @@ def include_end(self): self.input_data = self.filestack[-1][3] self.filestack.pop() - if self.filestack == []: # End of input? + if not self.filestack: # End of input? return self.filestack[-1][1] += 1 # Increment line counter of previous file @@ -339,12 +341,12 @@ def include_end(self): return result - def input(self, str, filename=''): + def input(self, str_, filename=''): """ Defines input string, removing current lexer. """ self.filestack.append([filename, 1, self.lex, self.input_data]) - self.input_data = str + self.input_data = str_ self.lex = lex.lex(object=self) self.lex.input(self.input_data) @@ -392,25 +394,15 @@ def find_column(self, token): return column - def msg(self, smsg): - """ Prints an error string msg. + def error(self, msg): + """ Prints an error msg and continues execution. """ - fname = os.path.basename(self.filestack[-1][0]) - line = self.lex.lineno - - OPTIONS.stderr.value.write('%s:%i %s\n' % (fname, line, smsg)) - - def error(self, str): - """ Prints an error msg, and exits. - """ - self.msg('Error: %s' % str) - - sys.exit(1) + error(self.lex.lineno, msg) - def warning(self, str): + def warning(self, msg): """ Emits a warning and continue execution. """ - self.msg('Warning: %s' % str) + warning(self.lex.lineno, msg) def __init__(self): """ Creates a new GLOBAL lexer instance @@ -423,7 +415,7 @@ def __init__(self): self.next_token = None # if set to something, this will be returned once -# --------------------- PREPROCESOR FUNCTIONS ------------------- +# --------------------- PREPROCESSOR FUNCTIONS ------------------- # Needed for states tmp = lex.lex(object=Lexer(), lextab='parsetab.zxbasmpplextab') diff --git a/zxblex.py b/zxblex.py index 534acfd54..51be26582 100755 --- a/zxblex.py +++ b/zxblex.py @@ -12,6 +12,7 @@ import sys from ply import lex from keywords import KEYWORDS as reserved +import api from api.errmsg import syntax_error @@ -31,6 +32,7 @@ # List of token names. _tokens = ( + 'ARRAY_ID', # This ID is a variable name from an array 'NUMBER', 'PLUS', 'MINUS', 'MUL', 'DIV', 'POW', 'LP', 'RP', 'LT', 'LBRACE', 'RBRACE', 'EQ', 'GT', 'LE', 'GE', 'NE', 'ID', @@ -454,7 +456,7 @@ def t_preproc_ID(t): def t_preproc_NEWLINE(t): r'\r?\n' t.lexer.begin('INITIAL') - t.lexer.lineno += len(t.value) + t.lexer.lineno += 1 return t @@ -490,13 +492,19 @@ def t_preproc_EQ(t): return t -# tokens regexp. patterns def t_ID(t): r'[a-zA-Z][a-zA-Z0-9]*[$%]?' t.type = reserved.get(t.value.lower(), 'ID') + callables = { + api.constants.CLASS.array: 'ARRAY_ID', + } if t.type != 'ID': t.value = t.type + else: + entry = api.global_.SYMBOL_TABLE.get_entry(t.value) if api.global_.SYMBOL_TABLE is not None else None + if entry: + t.type = callables.get(entry.class_, t.type) if t.type == 'BIN': t.lexer.begin('bin') @@ -575,26 +583,36 @@ def t_INITIAL_bin_LineContinue(t): LABELS_ALLOWED = False +# Separator skipped +def t_INITIAL_bin_preproc_SEPARATOR(t): + r'[ \t]+' + pass + + +def t_bin_ZERO(t): + r'[^01]' + t.lexer.begin('INITIAL') + t.type = 'NUMBER' + t.value = 0 + t.lexer.lexpos -= 1 + return t + + # track line numbers def t_INITIAL_bin_NEWLINE(t): r'\r?\n' global LABELS_ALLOWED t.lexer.lineno += 1 + t.value = '\n' LABELS_ALLOWED = True return t -# Separator skipped -def t_INITIAL_bin_preproc_SEPARATOR(t): - r'[ \t]+' - pass - - -def t_INITIAL_bin_string_asm_preproc_comment_ANYCHAR(t): +def t_INITIAL_bin_string_asm_preproc_comment_ERROR(t): r'.' syntax_error(t.lineno, "ignoring illegal character '%s'" % t.value[0]) - pass + return t # error handling rule diff --git a/zxbparser.py b/zxbparser.py index 48e9f091f..4b3ea2439 100755 --- a/zxbparser.py +++ b/zxbparser.py @@ -24,6 +24,7 @@ from api.errmsg import syntax_error from api.errmsg import warning +from api.check import check_and_make_label from api.check import common_type from api.check import is_dynamic from api.check import is_null @@ -42,6 +43,7 @@ import api.errmsg import api.symboltable import api.config +import api.utils # Symbol Classes import symbols @@ -102,6 +104,11 @@ # ---------------------------------------------------------------------- PRINT_IS_USED = False +# ---------------------------------------------------------------------- +# Last line number output for checking program key board BREAK +# ---------------------------------------------------------------------- +last_brk_linenum = 0 + def init(): """ Initializes parser state @@ -115,10 +122,12 @@ def init(): global data_ast global optemps global OPTIONS + global last_brk_linenum LABELS = {} LET_ASSIGNMENT = False PRINT_IS_USED = False + last_brk_linenum = 0 ast = None data_ast = None # Global Variables AST @@ -131,6 +140,14 @@ def init(): SYMBOL_TABLE = gl.SYMBOL_TABLE = api.symboltable.SymbolTable() OPTIONS = api.config.OPTIONS + # DATAs info + gl.DATA_LABELS.clear() + gl.DATA_IS_USED = False + del gl.DATAS[:] + gl.DATA_PTR_CURRENT = api.utils.current_data_label() + gl.DATA_FUNCTIONS = [] + gl.error_msg_cache.clear() + # ---------------------------------------------------------------------- # "Macro" functions. Just return more complex expresions @@ -185,8 +202,6 @@ def make_builtin(lineno, fname, operands, func=None, type_=None): operands = [] assert isinstance(operands, Symbol) or isinstance(operands, tuple) or isinstance(operands, list) # TODO: In the future, builtin functions will be implemented in an external library, like POINT or ATTR - # HINT: They are not yet, because Sinclair BASIC grammar allows not to use parenthesis e.g. SIN x = SIN(x) - # HINT: which requires syntactical changes in the parser __DEBUG__('Creating BUILTIN "{}"'.format(fname), 1) if not isinstance(operands, collections.Iterable): operands = [operands] @@ -198,15 +213,15 @@ def make_constexpr(lineno, expr): def make_strslice(lineno, s, lower, upper): - """ Wrapper: returns Strlice node + """ Wrapper: returns String Slice node """ return symbols.STRSLICE.make_node(lineno, s, lower, upper) -def make_sentence(sentence, *args): +def make_sentence(sentence, *args, **kwargs): """ Wrapper: returns a Sentence node """ - return symbols.SENTENCE(sentence, *args) + return symbols.SENTENCE(*([sentence] + list(args)), **kwargs) def make_asm_sentence(asm, lineno): @@ -234,10 +249,10 @@ def make_array_declaration(entry): return symbols.ARRAYDECL(entry) -def make_func_declaration(func_name, lineno): +def make_func_declaration(func_name, lineno, type_=None): """ This will return a node with the symbol as a function. """ - return symbols.FUNCDECL.make_node(func_name, lineno) + return symbols.FUNCDECL.make_node(func_name, lineno, type_=type_) def make_arg_list(node, *args): @@ -282,6 +297,37 @@ def make_array_access(id_, lineno, arglist): return symbols.ARRAYACCESS.make_node(id_, arglist, lineno) +def make_array_substr_assign(lineno, id_, arg_list, substr, expr_): + if arg_list is None or substr is None or expr_ is None: + return None # There were errors + + entry = SYMBOL_TABLE.access_call(id_, lineno) + if entry is None: + return None # There were errors + + if entry.type_ != TYPE.string: + syntax_error(lineno, "Array '%s' is not of type String" % id_) + return None # There were errors + + arr = make_array_access(id_, lineno, arg_list) + if arr is None: + return None # There were errors + + expr_ = make_typecast(arr.type_, expr_, lineno) + if expr_ is None: + return None # There were errors + + s0 = make_typecast(TYPE.uinteger, substr[0], lineno) + if s0 is None: + return None # There were errors + + s1 = make_typecast(TYPE.uinteger, substr[1], lineno) + if s1 is None: + return None # There were errors + + return make_sentence('LETARRAYSUBSTR', arr, s0, s1, expr_) + + def make_call(id_, lineno, args): """ This will return an AST node for a function call/array access. @@ -300,7 +346,7 @@ def make_call(id_, lineno, args): if entry is None: return None - if entry.class_ is CLASS.unknown and entry.type_ == TYPE.string and len(args) == 1: + if entry.class_ is CLASS.unknown and entry.type_ == TYPE.string and len(args) == 1 and is_numeric(args[0]): entry.class_ = CLASS.var # A scalar variable. e.g a$(expr) if entry.class_ == CLASS.array: # An already declared array @@ -325,8 +371,8 @@ def make_call(id_, lineno, args): return None if len(args) == 1: - return symbols.STRSLICE.make_node(lineno, entry, args[0].value, - args[0].value) + return symbols.STRSLICE.make_node(lineno, entry, args[0].value, args[0].value) + entry.accessed = True return entry @@ -370,13 +416,30 @@ def make_bound_list(node, *args): def make_label(id_, lineno): """ Creates a label entry. Returns None on error. """ - return SYMBOL_TABLE.declare_label(id_, lineno) + entry = SYMBOL_TABLE.declare_label(id_, lineno) + if entry: + gl.DATA_LABELS[id_] = gl.DATA_PTR_CURRENT # This label points to the current DATA block index + return entry + + +def make_break(lineno, p): + """ Checks if --enable-break is set, and if so, calls + BREAK keyboard interruption for this line if it has not been already + checked """ + global last_brk_linenum + + if not OPTIONS.enableBreak.value or lineno == last_brk_linenum or is_null(p): + return None + + last_brk_linenum = lineno + return make_sentence('CHKBREAK', make_number(lineno, lineno, TYPE.uinteger)) # ---------------------------------------------------------------------- # Operators precedence # ---------------------------------------------------------------------- precedence = ( + ('nonassoc', 'ID', 'ARRAY_ID'), ('left', 'OR'), ('left', 'AND'), ('left', 'XOR'), @@ -389,6 +452,12 @@ def make_label(id_, lineno): ('left', 'MUL', 'DIV'), ('right', 'UMINUS'), ('right', 'POW'), + ('left', 'RP'), + ('right', 'LP'), + ('right', 'ELSE'), + ('left', 'CO'), + ('left', 'NEWLINE'), + ('right', 'LABEL'), ) @@ -449,52 +518,98 @@ def p_start(p): def p_program_program_line(p): """ program : program_line """ - if OPTIONS.enableBreak.value: - lineno = p.lexer.lineno - tmp = make_sentence('CHKBREAK', - make_number(lineno, lineno, TYPE.uinteger)) - p[0] = make_block(p[1], tmp) - else: - p[0] = make_block(p[1]) + p[0] = make_block(p[1], make_break(p.lineno(1), p[1])) def p_program(p): """ program : program program_line """ - if OPTIONS.enableBreak.value: - lineno = p.lexer.lineno - tmp = make_sentence('CHKBREAK', - make_number(lineno, lineno, TYPE.uinteger)) - p[0] = make_block(p[1], p[2], tmp) + p[0] = make_block(p[1], p[2], make_break(p.lineno(2), p[2])) + + +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] + + +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() + + +def p_co_statements(p): + """ co_statements : co_statements_co statement + """ + p[0] = make_block(p[1], p[2]) + + +def p_statements_co(p): + """ statements_co : statements CO + | statements_co CO + """ + p[0] = p[1] + + +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 p_program_line_(p): - """ program_line : statement - | var_decl - | preproc_line - | label_line +def p_var_decls(p): + """ statement : var_decl """ p[0] = p[1] def p_program_line_label(p): - """ label_line : LABEL statement - | LABEL var_decl + """ label_line : LABEL statements + | LABEL co_statements + """ + lbl = make_label(p[1], p.lineno(1)) + p[0] = make_block(lbl, p[2]) if len(p) == 3 else lbl + + +def p_label_line_label_line_co(p): + """ label_line : label_line_co + """ + p[0] = p[1] + + +def p_label_line_co(p): + """ label_line_co : LABEL statements_co + | LABEL co_statements_co + | LABEL """ - p[0] = make_block(make_label(p[1], p.lineno(1)), p[2]) + lbl = make_label(p[1], p.lineno(1)) + p[0] = make_block(lbl, p[2]) if len(p) == 3 else lbl -def p_program_line_label2(p): - """ program_line : ID CO NEWLINE +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] = make_label(p[1], p.lineno(1)) + p[0] = p[1] if len(p) == 2 else make_block(p[1], p[2]) def p_var_decl(p): - """ var_decl : DIM idlist typedef NEWLINE - | DIM idlist typedef CO + """ var_decl : DIM idlist typedef """ for vardata in p[2]: SYMBOL_TABLE.declare_variable(vardata[0], vardata[1], p[3]) @@ -503,8 +618,7 @@ def p_var_decl(p): def p_var_decl_at(p): - """ var_decl : DIM idlist typedef AT expr CO - | DIM idlist typedef AT expr NEWLINE + """ var_decl : DIM idlist typedef AT expr """ p[0] = None @@ -540,15 +654,14 @@ def p_var_decl_at(p): return else: entry.addr = str(make_typecast(_TYPE(gl.STR_INDEX_TYPE), p[5], p.lineno(4)).value) + entry.accessed = True if entry.scope == SCOPE.local: SYMBOL_TABLE.make_static(entry.name) def p_var_decl_ini(p): - """ var_decl : DIM idlist typedef EQ expr NEWLINE - | DIM idlist typedef EQ expr CO - | CONST idlist typedef EQ expr NEWLINE - | CONST idlist typedef EQ expr CO + """ var_decl : DIM idlist typedef EQ expr + | CONST idlist typedef EQ expr """ p[0] = None if len(p[2]) != 1: @@ -556,6 +669,9 @@ def p_var_decl_ini(p): "Initialized variables must be declared one by one.") return + if p[5] is None: + return + if not is_static(p[5]): if isinstance(p[5], symbols.UNARY): p[5] = make_constexpr(p.lineno(4), p[5]) # Delayed constant evaluation @@ -573,53 +689,63 @@ def p_var_decl_ini(p): SYMBOL_TABLE.declare_const(p[2][0][0], p[2][0][1], p[3], default_value=defval) - if defval is None: # Okay do a delayed initalization + if defval is None: # Okay do a delayed initialization p[0] = make_sentence('LET', SYMBOL_TABLE.access_var(p[2][0][0], p.lineno(1)), value) def p_idlist_id(p): """ idlist : ID + | ARRAY_ID """ p[0] = [(p[1], p.lineno(1))] def p_idlist_idlist_id(p): """ idlist : idlist COMMA ID + | idlist COMMA ARRAY_ID """ p[0] = p[1] + [(p[3], p.lineno(3))] def p_arr_decl(p): - """ var_decl : DIM ID LP bound_list RP typedef NEWLINE - | DIM ID LP bound_list RP typedef CO + """ var_decl : var_arr_decl """ - SYMBOL_TABLE.declare_array(p[2], p.lineno(2), p[6], p[4]) p[0] = None +def p_decl_arr(p): + """ var_arr_decl : DIM idlist LP bound_list RP typedef + """ + if len(p[2]) != 1: + syntax_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 ID LP bound_list RP typedef RIGHTARROW const_vector NEWLINE - | DIM ID LP bound_list RP typedef RIGHTARROW const_vector CO - | DIM ID LP bound_list RP typedef EQ const_vector NEWLINE - | DIM ID LP bound_list RP typedef EQ const_vector CO + """ var_arr_decl : DIM idlist LP bound_list RP typedef RIGHTARROW const_vector + | DIM idlist LP bound_list RP typedef EQ const_vector """ 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 :-) - syntax_error(p.lineno(9), 'Unexpected extra vector dimensions. It should be %i' % len(remaining)) + syntax_error(lineno, 'Unexpected extra vector dimensions. It should be %i' % len(remaining)) if not isinstance(remaining, list): - syntax_error(p.lineno(9), 'Mismatched vector size. Missing %i extra dimension(s)' % len(boundlist)) + syntax_error(lineno, 'Mismatched vector size. Missing %i extra dimension(s)' % len(boundlist)) return False if len(remaining) != boundlist[0].count: - syntax_error(p.lineno(9), 'Mismatched vector size. Expected %i elements, got %i.' % (boundlist[0].count, - len(remaining))) + syntax_error(lineno, 'Mismatched vector size. Expected %i elements, got %i.' % (boundlist[0].count, + len(remaining))) return False # It's wrong. :-( for row in remaining: @@ -633,7 +759,8 @@ def check_bound(boundlist, remaining): return if check_bound(p[4].children, p[8]): - SYMBOL_TABLE.declare_array(p[2], p.lineno(2), p[6], p[4], default_value=p[8]) + id_, lineno = p[2][0] + SYMBOL_TABLE.declare_array(id_, lineno, p[6], p[4], default_value=p[8]) p[0] = None @@ -727,13 +854,6 @@ def p_const_vector_vector_list(p): p[0] = p[1] + [p[3]] -def p_empty_statement(p): - """ statement : NEWLINE - | CO - """ - p[0] = None - - def p_staement_func_decl(p): """ statement : function_declaration """ @@ -741,159 +861,146 @@ def p_staement_func_decl(p): def p_statement_border(p): - """ statement : BORDER expr NEWLINE - | BORDER expr CO + """ statement : BORDER expr """ p[0] = make_sentence('BORDER', - make_typecast(TYPE.ubyte, p[2], p.lineno(3))) + make_typecast(TYPE.ubyte, p[2], p.lineno(1))) def p_statement_plot(p): - """ statement : PLOT expr COMMA expr NEWLINE - | PLOT expr COMMA expr CO + """ statement : PLOT expr COMMA expr """ p[0] = make_sentence('PLOT', make_typecast(TYPE.ubyte, p[2], p.lineno(3)), - make_typecast(TYPE.ubyte, p[4], p.lineno(5))) + make_typecast(TYPE.ubyte, p[4], p.lineno(3))) def p_statement_plot_attr(p): - """ statement : PLOT attr_list expr COMMA expr NEWLINE - | PLOT attr_list expr COMMA expr CO + """ statement : PLOT attr_list expr COMMA expr """ p[0] = make_sentence('PLOT', make_typecast(TYPE.ubyte, p[3], p.lineno(4)), - make_typecast(TYPE.ubyte, p[5], p.lineno(6)), p[2]) + make_typecast(TYPE.ubyte, p[5], p.lineno(4)), p[2]) def p_statement_draw3(p): - """ statement : DRAW expr COMMA expr COMMA expr NEWLINE - | DRAW expr COMMA expr COMMA expr CO + """ statement : DRAW expr COMMA expr COMMA expr """ p[0] = make_sentence('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(7))) + make_typecast(TYPE.float_, p[6], p.lineno(5))) def p_statement_draw3_attr(p): - """ statement : DRAW attr_list expr COMMA expr COMMA expr NEWLINE - | DRAW attr_list expr COMMA expr COMMA expr CO + """ statement : DRAW attr_list expr COMMA expr COMMA expr """ p[0] = make_sentence('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(8)), p[2]) + make_typecast(TYPE.float_, p[7], p.lineno(6)), p[2]) def p_statement_draw(p): - """ statement : DRAW expr COMMA expr NEWLINE - | DRAW expr COMMA expr CO + """ statement : DRAW expr COMMA expr """ p[0] = make_sentence('DRAW', make_typecast(TYPE.integer, p[2], p.lineno(3)), - make_typecast(TYPE.integer, p[4], p.lineno(5))) + make_typecast(TYPE.integer, p[4], p.lineno(3))) def p_statement_draw_attr(p): - """ statement : DRAW attr_list expr COMMA expr NEWLINE - | DRAW attr_list expr COMMA expr CO + """ statement : DRAW attr_list expr COMMA expr """ p[0] = make_sentence('DRAW', make_typecast(TYPE.integer, p[3], p.lineno(4)), - make_typecast(TYPE.integer, p[5], p.lineno(6)), p[2]) + make_typecast(TYPE.integer, p[5], p.lineno(4)), p[2]) def p_statement_circle(p): - """ statement : CIRCLE expr COMMA expr COMMA expr NEWLINE - | CIRCLE expr COMMA expr COMMA expr CO + """ statement : CIRCLE expr COMMA expr COMMA expr """ p[0] = make_sentence('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(7))) + make_typecast(TYPE.byte_, p[6], p.lineno(5))) def p_statement_circle_attr(p): - """ statement : CIRCLE attr_list expr COMMA expr COMMA expr NEWLINE - | CIRCLE attr_list expr COMMA expr COMMA expr CO + """ statement : CIRCLE attr_list expr COMMA expr COMMA expr """ p[0] = make_sentence('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(8)), p[2]) + make_typecast(TYPE.byte_, p[7], p.lineno(6)), p[2]) def p_statement_cls(p): - """ statement : CLS NEWLINE - | CLS CO + """ statement : CLS """ p[0] = make_sentence('CLS') def p_statement_asm(p): - """ statement : ASM NEWLINE - | ASM CO + """ statement : ASM """ p[0] = make_asm_sentence(p[1], p.lineno(1)) def p_statement_randomize(p): - """ statement : RANDOMIZE NEWLINE - | RANDOMIZE CO + """ statement : RANDOMIZE """ - p[0] = make_sentence('RANDOMIZE', - make_number(0, lineno=p.lineno(1), type_=TYPE.ulong)) + p[0] = make_sentence('RANDOMIZE', make_number(0, lineno=p.lineno(1), type_=TYPE.ulong)) def p_statement_randomize_expr(p): - """ statement : RANDOMIZE expr NEWLINE - | RANDOMIZE expr CO + """ statement : RANDOMIZE expr """ - p[0] = make_sentence('RANDOMIZE', - make_typecast(TYPE.ulong, p[2], p.lineno(1))) + p[0] = make_sentence('RANDOMIZE', make_typecast(TYPE.ulong, p[2], p.lineno(1))) def p_statement_beep(p): - """ statement : BEEP expr COMMA expr NEWLINE - | BEEP expr COMMA expr CO + """ statement : BEEP expr COMMA expr """ p[0] = make_sentence('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 NEWLINE - | ID arg_list CO - | ID NEWLINE + """ statement : ID arg_list + | ID arguments + | ID """ - if p[2] is None: + if len(p) > 2 and p[2] is None: p[0] = None - elif len(p) == 3: - p[0] = make_sub_call(p[1], p.lineno(1), make_arg_list(None)) + elif len(p) == 2: + entry = SYMBOL_TABLE.get_entry(p[1]) + if not entry or entry.class_ in (CLASS.label, CLASS.unknown): + p[0] = make_label(p[1], p.lineno(1)) + else: + p[0] = make_sub_call(p[1], p.lineno(1), make_arg_list(None)) else: p[0] = make_sub_call(p[1], p.lineno(1), p[2]) def p_assignment(p): - """ statement : lexpr expr CO - | lexpr expr NEWLINE + """ statement : lexpr expr """ global LET_ASSIGNMENT LET_ASSIGNMENT = False # Mark we're no longer using LET p[0] = None q = p[1:] - i = 3 + i = 1 if q[1] is None: return if isinstance(q[1], symbols.VAR) and q[1].class_ == CLASS.unknown: - q[1] = SYMBOL_TABLE.access_var(q[1].name, p.lineno(3)) + q[1] = SYMBOL_TABLE.access_var(q[1].name, p.lineno(i)) q1class_ = q[1].class_ if isinstance(q[1], symbols.VAR) else CLASS.unknown - variable = SYMBOL_TABLE.access_id(q[0], p.lineno(3), default_type=q[1].type_, default_class=q1class_) + 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 @@ -901,7 +1008,7 @@ def p_assignment(p): variable.class_ = CLASS.var if variable.class_ not in (CLASS.var, CLASS.array): - syntax_error(p.lineno(i), "Cannot assign a value to '%s'. It's not a variable" % variable.name) + api.errmsg.syntax_error_cannot_assing_not_a_var(p.lineno(i), variable.name) return if variable.class_ == CLASS.var and q1class_ == CLASS.array: @@ -935,13 +1042,15 @@ def p_assignment(p): p[0] = make_sentence('ARRAYCOPY', variable, q[1]) return - expr = make_typecast(variable.type_, q[1], p.lineno(3)) + expr = make_typecast(variable.type_, q[1], p.lineno(i)) p[0] = make_sentence('LET', variable, expr) def p_lexpr(p): """ lexpr : ID EQ | LET ID EQ + | ARRAY_ID EQ + | LET ARRAY_ID EQ """ global LET_ASSIGNMENT @@ -958,70 +1067,112 @@ def p_lexpr(p): def p_arr_assignment(p): - """ statement : ID arg_list EQ expr CO - | ID arg_list EQ expr NEWLINE - | LET ID arg_list EQ expr CO - | LET ID arg_list EQ expr NEWLINE + """ statement : ARRAY_ID arg_list EQ expr + | LET ARRAY_ID arg_list EQ expr """ - q = p[1:] - i = 2 - if q[0].upper() == 'LET': - q = q[1:] - i = 3 - - if q[1] is None or q[3] is None: - return # There where errors + i = 2 if p[1].upper() == 'LET' else 1 + id_ = p[i] + arg_list = p[i + 1] + expr = p[i + 3] p[0] = None - # api.check.check_is_declared_strict(p.lineno(i - 1), q[0], classname='array') + if arg_list is None or expr is None: + return # There were errors - entry = SYMBOL_TABLE.access_call(q[0], p.lineno(i - 1)) + entry = SYMBOL_TABLE.access_call(id_, p.lineno(i)) if entry is None: - # variable = SYMBOL_TABLE.make_var(q[0], p.lineno(1), TYPE.string) - # entry = SYMBOL_TABLE.get_id_entry(q[0]) return - if entry.class_ == CLASS.var and entry.type_ == TYPE.string: - r = q[3] - if r.type_ != TYPE.string: - api.errmsg.syntax_error_expected_string(p.lineno(i - 1), r.type_) - - if len(q[1]) > 1: - syntax_error(p.lineno(i), "Too many values. Expected only one.") + if entry.type_ == TYPE.string: + variable = gl.SYMBOL_TABLE.access_array(id_, p.lineno(i)) + if len(variable.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 - if len(q[1]) == 1: - substr = ( - make_typecast(_TYPE(gl.STR_INDEX_TYPE), q[1][0].value, p.lineno(i)), - make_typecast(_TYPE(gl.STR_INDEX_TYPE), q[1][0].value, p.lineno(i))) - else: - substr = (make_typecast(_TYPE(gl.STR_INDEX_TYPE), - make_number(gl.MIN_STRSLICE_IDX, - lineno=p.lineno(i)), - p.lineno(i)), - make_typecast(_TYPE(gl.STR_INDEX_TYPE), - make_number(gl.MAX_STRSLICE_IDX, - lineno=p.lineno(i)), - p.lineno(i))) - - lineno = p.lineno(0) - base = make_number(OPTIONS.string_base.value, lineno, _TYPE(gl.STR_INDEX_TYPE)) - p[0] = make_sentence('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), - r) - return - - arr = make_array_access(q[0], p.lineno(i), q[1]) - expr = make_typecast(arr.type_, q[3], p.lineno(i)) + 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 + p[0] = make_sentence('LETARRAY', arr, expr) +def p_substr_assignment_no_let(p): + """ statement : ID LP expr RP EQ expr + """ + # This can be only a substr assignment like a$(i + 3) = ".", since arrays + # have ARRAY_ID already + entry = SYMBOL_TABLE.access_call(p[1], p.lineno(1)) + if entry is None: + return + + if entry.class_ == CLASS.unknown: + entry.class_ = CLASS.var + + if p[6].type_ != TYPE.string: + api.errmsg.syntax_error_expected_string(p.lineno(5), p[6].type_) + + lineno = p.lineno(2) + base = make_number(OPTIONS.string_base.value, lineno, _TYPE(gl.STR_INDEX_TYPE)) + substr = make_typecast(_TYPE(gl.STR_INDEX_TYPE), p[3], lineno) + p[0] = make_sentence('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.class_ = CLASS.var + + assert entry.class_ == CLASS.var and entry.type_ == TYPE.string + + if p[5].type_ != TYPE.string: + api.errmsg.syntax_error_expected_string(p.lineno(4), p[5].type_) + + if len(p[3]) > 1: + syntax_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.value, lineno, _TYPE(gl.STR_INDEX_TYPE)) + p[0] = make_sentence('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 CO - | ID substr EQ expr NEWLINE - | LET ID substr EQ expr CO - | LET ID substr EQ expr NEWLINE + """ statement : ID substr EQ expr + | LET ID substr EQ expr """ if p[1].upper() != 'LET': q = p[1] @@ -1050,22 +1201,10 @@ def p_str_assign(p): def p_goto(p): - """ statement : goto NUMBER CO - | goto NUMBER NEWLINE - | goto ID CO - | goto ID NEWLINE - """ - if isinstance(p[2], float): - if p[2] == int(p[2]): - id_ = str(int(p[2])) - else: - syntax_error(p.lineno(1), 'Line numbers must be integers.') - p[0] = None - return - else: - id_ = p[2] - - entry = SYMBOL_TABLE.access_label(id_, p.lineno(2)) + """ 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[1].upper(), entry) else: @@ -1083,240 +1222,196 @@ def p_go(p): p[0] += p[2] +# 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 = make_label(p[3], p.lineno(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('IF', cond_, make_block(stat_, endif_), lineno=p.lineno(2)) + + def p_endif(p): """ endif : END IF | LABEL END IF + | ENDIF + | LABEL ENDIF """ - if p[1] == 'END': - p[0] = make_nop() - else: - p[0] = make_label(p[1], p.lineno(1)) + p[0] = make_nop() if p[1] in ('END', 'ENDIF') else make_label(p[1], p.lineno(1)) -def p_if_sentence(p): - """ statement : IF expr then program endif CO - | IF expr then program endif NEWLINE +def p_statement_if(p): + """ statement : if_inline %prec RP """ - if is_null(p[4]): - warning(p.lineno(1), 'Useless empty IF ignored') - p[0] = p[5] - return + p[0] = p[1] - if is_number(p[2]): - api.errmsg.warning_condition_is_always(p.lineno(1), bool(p[2].value)) - if OPTIONS.optimization.value > 0: - if not p[2].value: - p[0] = p[5] - return - else: - p[0] = make_block(p[4], p[5]) - return - p[0] = make_sentence('IF', p[2], make_block(p[4], p[5])) +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('IF', cond_, make_block(stat_, endif_), lineno=p.lineno(1)) -def p_if_elseif(p): - """ statement : IF expr then program elseiflist CO - | IF expr then program elseiflist NEWLINE +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 """ - if is_null(p[4], p[5]): - p[0] = make_nop() - return - - if is_number(p[2]): - api.errmsg.warning_condition_is_always(p.lineno(1), bool(p[2].value)) - if OPTIONS.optimization.value > 0: - if not p[2].value: - p[0] = p[5] - return + cond_ = p[1] + stat_ = p[2] + p[0] = make_sentence('IF', cond_, stat_, lineno=p.lineno(1)) - if not p[4]: # Empty block? - p[4].appendChild(make_nop()) - p[0] = make_sentence('IF', p[2], p[4], p[5]) +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('IF', cond_, stats_, eliflist, lineno=p.lineno(2)) -def p_elseif_list(p): - """ elseiflist : ELSEIF expr then program endif - | LABEL ELSEIF expr then program endif +def p_elseif_part(p): + """ elseif_expr : ELSEIF expr then + | LABEL ELSEIF expr then """ if p[1] == 'ELSEIF': - p1 = make_nop() # No label - p2 = p[2] - p4 = p[4] - p5 = p[5] + label_ = make_nop() # No label + cond_ = p[2] else: - p1 = make_label(p[1], p.lineno(1)) - p2 = p[3] - p4 = p[5] - p5 = p[6] - - if is_null(p4): - warning(p.lineno(1), 'Useless empty ELSEIF ignored') - p[0] = make_block(p1, p5) - return - - if is_number(p2): - api.errmsg.warning_condition_is_always(p.lineno(1), bool(p2.value)) - if OPTIONS.optimization.value > 0: - if not p2.value: - p[0] = make_block(p1, p5) - return + label_ = make_label(p[1], p.lineno(1)) + cond_ = p[3] - p[0] = make_block(p1, make_sentence('IF', p2, make_block(p4, p5))) + p[0] = label_, cond_ -def p_elseif_elseiflist(p): - """ elseiflist : ELSEIF expr then program elseiflist - | LABEL ELSEIF expr then program elseiflist +def p_elseif_list(p): + """ elseiflist : elseif_expr program_co endif + | elseif_expr program_co else_part """ - if p[1] == 'ELSEIF': - p1 = make_nop() - p2 = p[2] - p4 = p[4] - p5 = p[5] - else: - p1 = make_label(p[1], p.lineno(1)) - p2 = p[3] - p4 = p[5] - p5 = p[6] + label_, cond_ = p[1] + then_ = p[2] + else_ = p[3] - if is_null(p4, p5): - p[0] = make_block(p1, p5) - return - - if is_number(p2) and p2.value == 0: - api.errmsg.warning_condition_is_always(p.lineno(1)) - if OPTIONS.optimization.value > 0: - p[0] = make_block(p1, p5) - return + 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(p1, make_sentence('IF', p2, p4, p5)) + p[0] = make_block(label_, make_sentence('IF', cond_, then_, else_, lineno=p.lineno(1))) -def p_else(p): - """ else : ELSE - | LABEL ELSE - """ - if p[1] == 'ELSE': - p[0] = make_nop() +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('IF', cond_, then_, else_, lineno=p.lineno(1))) + + +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] = [make_label(p[3], p.lineno(3)), p[4], p[5]] + else: + p[0] = [p[3], p[4]] else: - p[0] = make_label(p[1], p.lineno(1)) + p[0] = [p[2], p[3]] -def p_if_else(p): - """ statement : IF expr then program else program endif CO - | IF expr then program else program endif NEWLINE +def p_else_part(p): + """ else_part_inline : ELSE statements_co + | ELSE co_statements_co + | ELSE co_statements + | ELSE statements """ - if is_null(p[4], p[6]): - warning(p.lineno(1), 'Useless empty IF ignored') - p[0] = p[7] - return + p[0] = [p[2], make_nop()] - if is_number(p[2]): - api.errmsg.warning_condition_is_always(p.lineno(1), bool(p[2].value)) - if not p[2].value: - p[0] = make_block(p[6], p[7]) - return - else: - p[0] = make_block(p[4], p[7]) - return - - if is_null(p[4]): - p[2] = make_unary(p.lineno(1), 'NOT', p[2], lambda x: not x) - p[0] = make_sentence('IF', p[2], make_block(p[5], p[6], p[7])) - - if is_null(p[6]): - p[6] = make_nop() - p[0] = make_sentence('IF', p[2], p[4], make_block(p[5], p[6], p[7])) +def p_else_part_is_inline(p): + """ else_part : else_part_inline + """ + p[0] = p[1] -def p_if_elseif_else(p): - """ statement : IF expr then program elseif_elselist program endif CO - | IF expr then program elseif_elselist program endif NEWLINE +def p_else_part_label(p): + """ else_part : LABEL ELSE program_co endif + | LABEL ELSE statements_co endif + | LABEL ELSE co_statements_co endif """ - if is_number(p[2]) and p[2].value == 0: # Always false? - api.errmsg.warning_condition_is_always(p.lineno(1)) - if OPTIONS.optimization.value > 0: - if is_null(p[5]): # If no else part, return last parts - p[0] = make_block(p[6], p[7]) - return + lbl = make_label(p[1], p.lineno(1)) + p[0] = [make_block(lbl, p[3]), p[4]] - p[5][1].children[-1].appendChild(make_block(p[6], p[7])) - p[0] = p[5] - return - if is_null(p[5]): # Normal IF/THEN/ELSE - p[0] = make_sentence('IF', p[2], p[4], make_block(p[6], p[7])) - return +def p_if_then_part(p): + """ if_then_part : IF expr then """ + if is_number(p[2]): + api.errmsg.warning_condition_is_always(p.lineno(1), bool(p[2].value)) - p[5][1].children[-1].appendChild(make_block(p[6], p[7])) - p[0] = make_sentence('IF', p[2], p[4], p[5][0]) + p[0] = p[2] -def p_elseif_elselist_else(p): - """ elseif_elselist : ELSEIF expr then program else - | LABEL ELSEIF expr then program else +def p_if_inline(p): + """ statement : if_inline else_part_inline """ - if p[1] == 'ELSEIF': - p1 = make_nop() - p2 = p[2] - p4 = p[4] - p5 = p[5] - else: - p1 = make_label(p[1], p.lineno(1)) - p2 = p[3] - p4 = p[5] - p5 = p[6] - - if is_number(p2): - api.errmsg.warning_condition_is_always(p.lineno(1), bool(p2.value)) - if OPTIONS.optimization.value > 0: - if not p2.value: - p[0] = p1 - return - - # p[6] must be added in the rule above - last = make_block(p1, make_sentence('IF', p2, make_block(p4, p5))) - p[0] = (last, last) + p[1].appendChild(make_block(p[2][0], p[2][1])) + p[0] = p[1] -def p_elseif_elselist(p): - """ elseif_elselist : ELSEIF expr then program elseif_elselist - | LABEL ELSEIF expr then program elseif_elselist +def p_if_else(p): + """ statement : if_then_part NEWLINE program_co else_part """ - if p[1] == 'ELSEIF': - p1 = make_nop() - p2 = p[2] - p4 = p[4] - p5 = p[5] - else: - p1 = make_label(p[1], p.lineno(1)) - p2 = p[3] - p4 = p[5] - p5 = p[6] - - if is_number(p2) and p2.value == 0: - api.errmsg.warning_condition_is_always(p.lineno(1)) - if OPTIONS.optimization.value > 0: - last = p5[1] - p[0] = (make_block(p1, p5[0]), last) - return - - node = make_sentence('IF', p2, p4, p5[0]) - p[0] = (make_block(p1, node), p5[1]) + cond_ = p[1] + then_ = p[3] + else_ = p[4][0] + endif = p[4][1] + p[0] = make_sentence('IF', cond_, then_, make_block(else_, endif), lineno=p.lineno(2)) def p_then(p): """ then : | THEN """ +# endregion +# region [FOR sentence] def p_for_sentence(p): - """ statement : for_start program label_next CO - | for_start program label_next NEWLINE + """ statement : for_start program_co label_next + | for_start co_statements_co label_next """ p[0] = p[1] if is_null(p[0]): @@ -1329,10 +1424,7 @@ def p_next(p): """ label_next : LABEL NEXT | NEXT """ - if p[1] == 'NEXT': - p[0] = make_nop() - else: - p[0] = make_label(p[1], p.lineno(1)) + p[0] = make_nop() if p[1] == 'NEXT' else make_label(p[1], p.lineno(1)) def p_next1(p): @@ -1354,72 +1446,26 @@ def p_next1(p): p[0] = p1 -def p_end(p): - """ statement : END expr CO - | END expr NEWLINE - | END CO - | END NEWLINE - """ - q = p[2] - if not isinstance(q, Symbol): - q = make_number(0, lineno=p.lineno(1)) - p[0] = make_sentence('END', q) - - -def p_error_raise(p): - """ statement : ERROR expr CO - | ERROR expr NEWLINE - """ - q = make_number(1, lineno=p.lineno(3)) - 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('ERROR', r) - - -def p_stop_raise(p): - """ statement : STOP expr CO - | STOP expr NEWLINE - | STOP CO - | STOP NEWLINE - """ - q = p[2] - if not isinstance(q, Symbol): - q = make_number(9, lineno=p.lineno(1)) - - 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('STOP', r) - - def p_for_sentence_start(p): """ for_start : FOR ID EQ expr TO expr step """ - # api.check.check_is_declared(p.lineno(2), p[2]) gl.LOOPS.append(('FOR', 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 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 OPTIONS.optimizations > 0: - return 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') - if OPTIONS.optimizations > 0: - return id_type = common_type(common_type(p[4], p[6]), p[7]) - variable = SYMBOL_TABLE.access_var(p[2], p.lineno(2), default_type=id_type) if variable is None: return @@ -1442,6 +1488,37 @@ 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('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('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('STOP', r) def p_loop(p): @@ -1455,14 +1532,11 @@ def p_loop(p): def p_do_loop(p): - """ statement : do_start program label_loop CO - | do_start program label_loop NEWLINE - | do_start label_loop CO - | do_start label_loop NEWLINE - | DO label_loop CO - | DO label_loop NEWLINE - """ - if len(p) > 4: + """ 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] @@ -1479,14 +1553,11 @@ def p_do_loop(p): def p_do_loop_until(p): - """ statement : do_start program label_loop UNTIL expr CO - | do_start program label_loop UNTIL expr NEWLINE - | do_start label_loop UNTIL expr CO - | do_start label_loop UNTIL expr NEWLINE - | DO label_loop UNTIL expr NEWLINE - | DO label_loop UNTIL expr CO - """ - if len(p) > 6: + """ 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: @@ -1505,15 +1576,110 @@ def p_do_loop_until(p): api.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 + + for d in p[2].children: + value = d.value + if is_static(value): + datas_.append(d) + continue + + new_lbl = '__DATA__FUNCPTR__{0}'.format(len(gl.DATA_FUNCTIONS)) + entry = make_func_declaration(new_lbl, p.lineno(1), type_=value.type_) + if not entry: + continue + + func = entry.entry + func.convention = CONVENTION.fastcall + SYMBOL_TABLE.enter_scope(new_lbl) + func.local_symbol_table = SYMBOL_TABLE.table[SYMBOL_TABLE.current_scope] + func.locals_size = SYMBOL_TABLE.leave_scope() + + gl.DATA_FUNCTIONS.append(func) + sent = make_sentence('RETURN', func, value) + func.body = make_block(sent) + datas_.append(entry) + funcs.append(entry) + + gl.DATAS.append([label_, datas_]) + id_ = api.utils.current_data_label() + gl.DATA_PTR_CURRENT = id_ + + +def p_restore(p): + """ statement : RESTORE + | RESTORE ID + | RESTORE NUMBER + """ + if len(p) == 2: + id_ = '__DATA__{0}'.format(len(gl.DATAS)) + else: + id_ = p[2] + + lbl = check_and_make_label(id_, p.lineno(1)) + p[0] = make_sentence('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 isinstance(entry, symbols.VARARRAY): + api.errmsg.syntax_error(p.lineno(1), "Cannot read '%s'. It's an array" % entry.name) + p[0] = None + return + + if isinstance(entry, symbols.VAR): + if entry.class_ != CLASS.var: + api.errmsg.syntax_error_cannot_assing_not_a_var(p.lineno(2), entry.name) + p[0] = None + return + + entry.accessed = True + if entry.type_ == TYPE.auto: + entry.type_ = _TYPE(gl.DEFAULT_TYPE) + api.errmsg.warning_implicit_type(p.lineno(2), p[2], entry.type_) + + reads.append(make_sentence('READ', entry)) + continue + + if isinstance(entry, symbols.ARRAYLOAD): + reads.append(make_sentence('READ', symbols.ARRAYACCESS(entry.entry, entry.args, entry.lineno))) + continue + + api.errmsg.syntax_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 label_loop WHILE expr CO - | do_start program label_loop WHILE expr NEWLINE - | do_start label_loop WHILE expr CO - | do_start label_loop WHILE expr NEWLINE - | DO label_loop WHILE expr NEWLINE - | DO label_loop WHILE expr CO - """ - if len(p) > 6: + """ 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: @@ -1533,10 +1699,9 @@ def p_do_loop_while(p): def p_do_while_loop(p): - """ statement : do_while_start program LOOP CO - | do_while_start program LOOP NEWLINE - | do_while_start LOOP CO - | do_while_start LOOP NEWLINE + """ statement : do_while_start program_co LOOP + | do_while_start co_statements_co LOOP + | do_while_start LOOP """ r = p[1] q = p[2] @@ -1551,10 +1716,9 @@ def p_do_while_loop(p): def p_do_until_loop(p): - """ statement : do_until_start program LOOP CO - | do_until_start program LOOP NEWLINE - | do_until_start LOOP CO - | do_until_start LOOP NEWLINE + """ statement : do_until_start program_co LOOP + | do_until_start co_statements_co LOOP + | do_until_start LOOP """ r = p[1] q = p[2] @@ -1569,16 +1733,14 @@ def p_do_until_loop(p): def p_do_while_start(p): - """ do_while_start : DO WHILE expr CO - | DO WHILE expr NEWLINE + """ do_while_start : DO WHILE expr """ p[0] = p[3] gl.LOOPS.append(('DO',)) def p_do_until_start(p): - """ do_until_start : DO UNTIL expr CO - | DO UNTIL expr NEWLINE + """ do_until_start : DO UNTIL expr """ p[0] = p[3] gl.LOOPS.append(('DO',)) @@ -1592,10 +1754,10 @@ def p_do_start(p): def p_label_end_while(p): - """ label_end_while : LABEL END WHILE - | LABEL WEND - | END WHILE + """ label_end_while : LABEL WEND + | LABEL END WHILE | WEND + | END WHILE """ if p[1] in ('WEND', 'END'): p[0] = None @@ -1604,34 +1766,19 @@ def p_label_end_while(p): def p_while_sentence(p): - """ statement : while_start program label_end_while CO - | while_start program label_end_while NEWLINE - | while_start label_end_while CO - | while_start label_end_while NEWLINE + """ 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 len(p) > 4: - q = make_block(p[2], p[3]) - else: - q = p[2] - - if is_number(p[1]): - if p[1].value == 0: - api.errmsg.warning_condition_is_always(p[1].lineno) - if OPTIONS.optimization.value > 0: - warning(p[1].lineno, "Loop has been ignored") - p[0] = None - else: - p[0] = make_sentence('WHILE', p[1], q) + if is_number(p[1]) and p[1].value: + if q is None: + warning(p[1].lineno, "Condition is always true and leads to an infinite loop.") else: - p[0] = make_sentence('WHILE', p[1], q) - if q is None: - warning(p[1].lineno, "Condition is always true and leads to an infinite loop.") - else: - warning(p[1].lineno, "Condition is always true and might lead to an infinite loop.") - else: - p[0] = make_sentence('WHILE', p[1], q) + warning(p[1].lineno, "Condition is always true and might lead to an infinite loop.") + + p[0] = make_sentence('WHILE', p[1], q) def p_while_start(p): @@ -1639,15 +1786,14 @@ def p_while_start(p): """ p[0] = p[2] gl.LOOPS.append(('WHILE',)) + if is_number(p[2]) and not p[2].value: + api.errmsg.warning_condition_is_always(p.lineno(1)) def p_exit(p): - """ statement : EXIT WHILE CO - | EXIT WHILE NEWLINE - | EXIT DO CO - | EXIT DO NEWLINE - | EXIT FOR CO - | EXIT FOR NEWLINE + """ statement : EXIT WHILE + | EXIT DO + | EXIT FOR """ q = p[2] p[0] = make_sentence('EXIT_%s' % q) @@ -1660,12 +1806,9 @@ def p_exit(p): def p_continue(p): - """ statement : CONTINUE WHILE CO - | CONTINUE WHILE NEWLINE - | CONTINUE DO CO - | CONTINUE DO NEWLINE - | CONTINUE FOR CO - | CONTINUE FOR NEWLINE + """ statement : CONTINUE WHILE + | CONTINUE DO + | CONTINUE FOR """ q = p[2] p[0] = make_sentence('CONTINUE_%s' % q) @@ -1678,8 +1821,7 @@ def p_continue(p): def p_print_sentence(p): - """ statement : PRINT print_list CO - | PRINT print_list NEWLINE + """ statement : PRINT print_list """ global PRINT_IS_USED @@ -1779,15 +1921,38 @@ def p_print_list_tab(p): 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('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 CO - | RETURN NEWLINE + """ statement : RETURN """ if not FUNCTION_LEVEL: # At less one level, otherwise, this return is from a GOSUB p[0] = make_sentence('RETURN') return - if FUNCTION_LEVEL[-1].kind != 'sub': + if FUNCTION_LEVEL[-1].kind != KIND.sub: syntax_error(p.lineno(1), 'Syntax Error: Functions must RETURN a value, or use EXIT FUNCTION instead.') p[0] = None return @@ -1796,8 +1961,7 @@ def p_return(p): def p_return_expr(p): - """ statement : RETURN expr CO - | RETURN expr NEWLINE + """ statement : RETURN expr """ if not FUNCTION_LEVEL: # At less one level syntax_error(p.lineno(1), 'Syntax Error: Returning value out of FUNCTION') @@ -1808,7 +1972,7 @@ def p_return_expr(p): p[0] = None return - if FUNCTION_LEVEL[-1].kind != 'function': + if FUNCTION_LEVEL[-1].kind != KIND.function: syntax_error(p.lineno(1), 'Syntax Error: SUBs cannot return a value') p[0] = None return @@ -1829,18 +1993,15 @@ def p_return_expr(p): def p_pause(p): - """ statement : PAUSE expr CO - | PAUSE expr NEWLINE + """ statement : PAUSE expr """ p[0] = make_sentence('PAUSE', make_typecast(TYPE.uinteger, p[2], p.lineno(1))) def p_poke(p): - """ statement : POKE expr COMMA expr CO - | POKE expr COMMA expr NEWLINE - | POKE LP expr COMMA expr RP CO - | POKE LP expr COMMA expr RP NEWLINE + """ 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: @@ -1848,30 +2009,26 @@ def p_poke(p): return p[0] = make_sentence('POKE', make_typecast(TYPE.uinteger, p[i], p.lineno(i + 1)), - make_typecast(TYPE.ubyte, p[i + 2], p.lineno(i + 3))) + make_typecast(TYPE.ubyte, p[i + 2], p.lineno(i + 1))) def p_poke2(p): - """ statement : POKE numbertype expr COMMA expr CO - | POKE numbertype expr COMMA expr NEWLINE - | POKE LP numbertype expr COMMA expr RP CO - | POKE LP numbertype expr COMMA expr RP NEWLINE + """ 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('POKE', - make_typecast(TYPE.uinteger, p[i + 1], - p.lineno(i + 2)), - make_typecast(p[i], p[i + 3], p.lineno(i + 4))) + 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 CO - | POKE numbertype COMMA expr COMMA expr NEWLINE - | POKE LP numbertype COMMA expr COMMA expr RP CO - | POKE LP numbertype COMMA expr COMMA expr RP NEWLINE + """ 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: @@ -1884,40 +2041,30 @@ def p_poke3(p): def p_out(p): - """ statement : OUT expr COMMA expr CO - | OUT expr COMMA expr NEWLINE + """ statement : OUT expr COMMA expr """ p[0] = make_sentence('OUT', make_typecast(TYPE.uinteger, p[2], p.lineno(3)), - make_typecast(TYPE.ubyte, p[4], p.lineno(5))) + make_typecast(TYPE.ubyte, p[4], p.lineno(4))) def p_simple_instruction(p): - """ statement : ITALIC expr CO - | ITALIC expr NEWLINE - | BOLD expr CO - | BOLD expr NEWLINE - | INK expr CO - | INK expr NEWLINE - | PAPER expr CO - | PAPER expr NEWLINE - | BRIGHT expr CO - | BRIGHT expr NEWLINE - | FLASH expr CO - | FLASH expr NEWLINE - | OVER expr CO - | OVER expr NEWLINE - | INVERSE expr CO - | INVERSE expr NEWLINE - """ - p[0] = make_sentence(p[1], make_typecast(TYPE.ubyte, p[2], p.lineno(3))) + """ statement : ITALIC expr + | BOLD expr + | INK expr + | PAPER expr + | BRIGHT expr + | FLASH expr + | OVER expr + | INVERSE expr + """ + p[0] = make_sentence(p[1], make_typecast(TYPE.ubyte, p[2], p.lineno(1))) def p_save_code(p): - """ statement : SAVE expr CODE expr COMMA expr CO - | SAVE expr CODE expr COMMA expr NEWLINE - | SAVE expr ID NEWLINE - | SAVE expr ID CO + """ statement : SAVE expr CODE expr COMMA expr + | SAVE expr ID + | SAVE expr ARRAY_ID """ if p[2].type_ != TYPE.string: api.errmsg.syntax_error_expected_string(p.lineno(1), p[2].type_) @@ -1939,17 +2086,14 @@ def p_save_code(p): def p_save_data(p): - """ statement : SAVE expr DATA CO - | SAVE expr DATA NEWLINE - | SAVE expr DATA ID CO - | SAVE expr DATA ID NEWLINE - | SAVE expr DATA ID LP RP CO - | SAVE expr DATA ID LP RP NEWLINE + """ statement : SAVE expr DATA + | SAVE expr DATA ID + | SAVE expr DATA ID LP RP """ if p[2].type_ != TYPE.string: api.errmsg.syntax_error_expected_string(p.lineno(1), p[2].type_) - if len(p) != 5: + if len(p) != 4: entry = SYMBOL_TABLE.access_id(p[4], p.lineno(4)) if entry is None: p[0] = None @@ -1964,12 +2108,11 @@ def p_save_data(p): else: length = make_number(entry.type_.size, lineno=p.lineno(4)) else: - access = SYMBOL_TABLE.access_id('.ZXBASIC_USER_DATA', p.lineno(4)) - start = make_unary(p.lineno(4), 'ADDRESS', access, type_=TYPE.uinteger) + access = SYMBOL_TABLE.access_id('.ZXBASIC_USER_DATA', p.lineno(3)) + start = make_unary(p.lineno(3), 'ADDRESS', access, type_=TYPE.uinteger) - access = SYMBOL_TABLE.access_id('.ZXBASIC_USER_DATA_LEN', p.lineno(4)) - length = make_unary(p.lineno(4), 'ADDRESS', access, - type_=TYPE.uinteger) + access = SYMBOL_TABLE.access_id('.ZXBASIC_USER_DATA_LEN', p.lineno(3)) + length = make_unary(p.lineno(3), 'ADDRESS', access, type_=TYPE.uinteger) p[0] = make_sentence(p[1], p[2], start, length) @@ -1982,19 +2125,15 @@ def p_load_or_verify(p): def p_load_code(p): - """ statement : load_or_verify expr ID CO - | load_or_verify expr CODE CO - | load_or_verify expr CODE expr CO - | load_or_verify expr CODE expr COMMA expr CO - | load_or_verify expr ID NEWLINE - | load_or_verify expr CODE NEWLINE - | load_or_verify expr CODE expr NEWLINE - | load_or_verify expr CODE expr COMMA expr NEWLINE + """ 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: api.errmsg.syntax_error_expected_string(p.lineno(3), p[2].type_) - if len(p) == 5: + if len(p) == 4: if p[3].upper() not in ('SCREEN', 'SCREEN$', 'CODE'): syntax_error(p.lineno(3), 'Unexpected "%s" ID. Expected "SCREEN$" instead' % p[3]) return None @@ -2008,7 +2147,7 @@ def p_load_code(p): else: start = make_typecast(TYPE.uinteger, p[4], p.lineno(3)) - if len(p) == 6: + if len(p) == 5: length = make_number(0, lineno=p.lineno(3)) else: length = make_typecast(TYPE.uinteger, p[6], p.lineno(5)) @@ -2017,17 +2156,14 @@ def p_load_code(p): def p_load_data(p): - """ statement : load_or_verify expr DATA CO - | load_or_verify expr DATA NEWLINE - | load_or_verify expr DATA ID CO - | load_or_verify expr DATA ID NEWLINE - | load_or_verify expr DATA ID LP RP CO - | load_or_verify expr DATA ID LP RP NEWLINE + """ 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: api.errmsg.syntax_error_expected_string(p.lineno(1), p[2].type_) - if len(p) != 5: + if len(p) != 4: entry = SYMBOL_TABLE.access_id(p[4], p.lineno(4)) if entry is None: p[0] = None @@ -2041,12 +2177,11 @@ def p_load_data(p): else: length = make_number(entry.type_.size, lineno=p.lineno(4)) else: - entry = SYMBOL_TABLE.access_id('.ZXBASIC_USER_DATA', p.lineno(4)) - start = make_unary(p.lineno(4), 'ADDRESS', entry, type_=TYPE.uinteger) + entry = SYMBOL_TABLE.access_id('.ZXBASIC_USER_DATA', p.lineno(3)) + start = make_unary(p.lineno(3), 'ADDRESS', entry, type_=TYPE.uinteger) - entry = SYMBOL_TABLE.access_id('.ZXBASIC_USER_DATA_LEN', p.lineno(4)) - length = make_unary(p.lineno(4), 'ADDRESS', entry, - type_=TYPE.uinteger) + entry = SYMBOL_TABLE.access_id('.ZXBASIC_USER_DATA_LEN', p.lineno(3)) + length = make_unary(p.lineno(3), 'ADDRESS', entry, type_=TYPE.uinteger) p[0] = make_sentence(p[1], p[2], start, length) @@ -2224,7 +2359,7 @@ def p_BNOT_expr(p): def p_lp_expr_rp(p): - """ expr : LP expr RP + """ bexpr : LP expr RP %prec ID """ p[0] = p[2] @@ -2236,25 +2371,25 @@ def p_cast(p): def p_number_expr(p): - """ expr : NUMBER + """ bexpr : NUMBER """ p[0] = make_number(p[1], lineno=p.lineno(1)) def p_expr_PI(p): - """ expr : PI + """ bexpr : PI """ p[0] = make_number(PI, lineno=p.lineno(1), type_=TYPE.float_) def p_number_line(p): - """ expr : __LINE__ + """ bexpr : __LINE__ """ p[0] = make_number(p.lineno(1), lineno=p.lineno(1)) def p_expr_string(p): - """ expr : string + """ bexpr : string %prec ID """ p[0] = p[1] @@ -2265,6 +2400,12 @@ def p_string_func_call(p): 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 """ @@ -2304,13 +2445,13 @@ def p_string_substr(p): def p_string_expr_lp(p): """ string : LP expr RP substr """ - if p[1].type_ != TYPE.string: + if p[2].type_ != TYPE.string: syntax_error(p.lexer.lineno, - "Expected a TYPE.string type expression. " - "Got '%s' one instead" % TYPE.to_string(p[1].type_)) + "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[1], p[2][0], p[2][1]) + p[0] = make_strslice(p.lexer.lineno, p[2], p[4][0], p[4][1]) def p_subind_str(p): @@ -2350,13 +2491,14 @@ def p_subind_TO(p): def p_exprstr_file(p): - """ expr : __FILE__ + """ bexpr : __FILE__ """ p[0] = symbols.STRING(gl.FILENAME, p.lineno(1)) def p_id_expr(p): - """ expr : ID + """ bexpr : ID + | ARRAY_ID """ entry = SYMBOL_TABLE.access_id(p[1], p.lineno(1), default_class=CLASS.var) if entry is None: @@ -2388,7 +2530,8 @@ def p_id_expr(p): def p_addr_of_id(p): - """ expr : ADDRESSOF ID + """ bexpr : ADDRESSOF ID + | ADDRESSOF ARRAY_ID """ entry = SYMBOL_TABLE.access_id(p[2], p.lineno(2)) if entry is None: @@ -2404,15 +2547,21 @@ def p_addr_of_id(p): p[0] = make_constexpr(p.lineno(1), result) +def p_expr_bexpr(p): + """ expr : bexpr + """ + p[0] = p[1] + + def p_expr_funccall(p): - """ expr : func_call + """ bexpr : func_call %prec ID """ p[0] = p[1] def p_idcall_expr(p): - """ func_call : ID arg_list - """ # This can be a function call, an array call or a string index + """ func_call : ID arg_list %prec UMINUS + """ # This can be a function call or a string index p[0] = make_call(p[1], p.lineno(1), p[2]) if p[0] is None: return @@ -2429,8 +2578,100 @@ def p_idcall_expr(p): p[0].entry.accessed = True -def p_addr_of_func_call(p): - """ expr : ADDRESSOF ID arg_list +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)) + entry.accessed = True + + +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 @@ -2445,6 +2686,33 @@ def p_addr_of_func_call(p): 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 + """ + syntax_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)) + entry.accessed = True + return + + # TODO: Check that arrays really needs kind=function to be set + # Both array accesses and functions are tagged as functions + # functions also has the class_ attribute set to 'function' + p[0].entry.set_kind(KIND.function, p.lineno(1)) + p[0].entry.accessed = True + + def p_arg_list(p): """ arg_list : LP RP """ @@ -2467,7 +2735,7 @@ def p_arguments(p): def p_argument(p): - """ arguments : expr + """ arguments : expr %prec ID """ p[0] = make_arg_list(make_argument(p[1], p.lineno(1))) @@ -2486,12 +2754,11 @@ def p_funcdecl(p): p[0].entry.body = p[2] entry = p[0].entry - if entry.forwarded: - entry.forwarded = False + entry.forwarded = False def p_funcdeclforward(p): - """ function_declaration : DECLARE function_header + """ function_declaration : DECLARE function_header_pre """ if p[2] is None: if FUNCTION_LEVEL: @@ -2507,8 +2774,14 @@ def p_funcdeclforward(p): def p_function_header(p): - """ function_header : function_def param_decl typedef NEWLINE - | function_def param_decl typedef CO + """ function_header : function_header_pre CO + | function_header_pre NEWLINE + """ + p[0] = p[1] + + +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 @@ -2519,13 +2792,16 @@ def p_function_header(p): p[0] = p[1] p[0].appendChild(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.kind == KIND.function: + api.errmsg.warning_implicit_type(p[3].lineno, p[0].entry.name, p[0].type_) if forwarded and previoustype_ != p[0].type_: - api.errmsg.syntax_error_func_type_mismatch(p.lineno(4), p[0].entry) + api.errmsg.syntax_error_func_type_mismatch(lineno, p[0].entry) p[0] = None return @@ -2534,35 +2810,38 @@ def p_function_header(p): p2 = p[2].children if len(p1) != len(p2): - api.errmsg.syntax_error_parameter_mismatch(p.lineno(4), p[0].entry) + api.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(p.lineno(4), "Parameter '%s' in function '%s' has been renamed to '%s'" % + 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: - api.errmsg.syntax_error_parameter_mismatch(p.lineno(4), p[0].entry) + api.errmsg.syntax_error_parameter_mismatch(lineno, p[0].entry) p[0] = None return p[0].entry.params = p[2] if FUNCTION_LEVEL[-1].kind == KIND.sub and not p[3].implicit: - syntax_error(p.lineno(4), 'SUBs cannot have a return type definition') + syntax_error(lineno, 'SUBs cannot have a return type definition') p[0] = None return + if FUNCTION_LEVEL[-1].kind == KIND.function: + 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: kind = 'SUB' if FUNCTION_LEVEL[-1].kind == KIND.sub else 'FUNCTION' - warning(p.lineno(4), "%s '%s' declared as FASTCALL with %i parameters" % (kind, p[0].entry.name, - len(p[2]))) + warning(lineno, "%s '%s' declared as FASTCALL with %i parameters" % (kind, p[0].entry.name, + len(p[2]))) def p_function_error(p): - """ function_declaration : function_header program END error NEWLINE + """ function_declaration : function_header program_co END error """ p[0] = None syntax_error(p.lineno(3), "Unexpected token 'END'. Expected 'END FUNCTION' or 'END SUB' instead.") @@ -2655,12 +2934,18 @@ def p_param_definition(p): def p_param_def_type(p): """ param_def : ID typedef """ + if p[2] is not None: + api.check.check_type_is_explicit(p.lineno(1), p[1], p[2]) p[0] = make_param_decl(p[1], p.lineno(1), p[2]) def p_function_body(p): - """ function_body : program END FUNCTION - | program END SUB + """ 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 """ @@ -2712,7 +2997,7 @@ def p_type(p): # Some preprocessor directives def p_preprocessor_line(p): - """ preproc_line : preproc_line_line NEWLINE + """ preproc_line : preproc_line_line """ @@ -2761,6 +3046,7 @@ def p_preproc_line_pop(p): OPTIONS.option(p[4]).pop() +# region INTERNAL FUNCTIONS # ---------------------------------------- # INTERNAL BASIC Functions # These will be implemented in the TRADuctor @@ -2768,7 +3054,7 @@ def p_preproc_line_pop(p): # ---------------------------------------- def p_expr_usr(p): - """ expr : USR expr %prec UMINUS + """ 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) @@ -2779,14 +3065,14 @@ def p_expr_usr(p): def p_expr_rnd(p): - """ expr : RND - | RND LP RP + """ bexpr : RND %prec ID + | RND LP RP """ p[0] = make_builtin(p.lineno(1), 'RND', None, type_=TYPE.float_) def p_expr_peek(p): - """ expr : PEEK expr %prec UMINUS + """ bexpr : PEEK bexpr %prec UMINUS """ p[0] = make_builtin(p.lineno(1), 'PEEK', make_typecast(TYPE.uinteger, p[2], p.lineno(1)), @@ -2794,7 +3080,7 @@ def p_expr_peek(p): def p_expr_peektype_(p): - """ expr : PEEK LP numbertype COMMA expr RP + """ bexpr : PEEK LP numbertype COMMA expr RP """ p[0] = make_builtin(p.lineno(1), 'PEEK', make_typecast(TYPE.uinteger, p[5], p.lineno(4)), @@ -2802,7 +3088,7 @@ def p_expr_peektype_(p): def p_expr_in(p): - """ expr : IN expr %prec UMINUS + """ bexpr : IN bexpr %prec UMINUS """ p[0] = make_builtin(p.lineno(1), 'IN', make_typecast(TYPE.uinteger, p[2], p.lineno(1)), @@ -2810,8 +3096,8 @@ def p_expr_in(p): def p_expr_lbound(p): - """ expr : LBOUND LP ID RP - | UBOUND LP ID RP + """ 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: @@ -2829,8 +3115,8 @@ def p_expr_lbound(p): def p_expr_lbound_expr(p): - """ expr : LBOUND LP ID COMMA expr RP - | UBOUND LP ID COMMA expr RP + """ bexpr : LBOUND LP ARRAY_ID COMMA expr RP + | UBOUND LP ARRAY_ID COMMA expr RP """ entry = SYMBOL_TABLE.access_array(p[3], p.lineno(3)) if entry is None: @@ -2866,7 +3152,7 @@ def p_expr_lbound_expr(p): def p_len(p): - """ expr : LEN expr %prec UMINUS + """ bexpr : LEN bexpr %prec UMINUS """ arg = p[2] if arg is None: @@ -2883,8 +3169,9 @@ def p_len(p): def p_sizeof(p): - """ expr : SIZEOF LP type RP + """ 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())), @@ -2895,13 +3182,13 @@ def p_sizeof(p): def p_str(p): - """ string : STR LP expr RP %prec UMINUS + """ string : STR expr %prec UMINUS """ - if is_number(p[3]): # A constant is converted to string directly - p[0] = symbols.STRING(str(p[3].value), p.lineno(1)) + if is_number(p[2]): # A constant is converted to string directly + p[0] = symbols.STRING(str(p[2].value), p.lineno(1)) else: p[0] = make_builtin(p.lineno(1), 'STR', - make_typecast(TYPE.float_, p[3], p.lineno(2)), + make_typecast(TYPE.float_, p[2], p.lineno(1)), type_=TYPE.string) @@ -2912,7 +3199,7 @@ def p_inkey(p): def p_chr_one(p): - """ string : CHR expr %prec UMINUS + """ 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)) @@ -2934,7 +3221,7 @@ def p_chr(p): def p_val(p): - """ expr : VAL expr %prec UMINUS + """ bexpr : VAL bexpr %prec UMINUS """ def val(s): @@ -2953,7 +3240,7 @@ def val(s): def p_code(p): - """ expr : CODE expr %prec UMINUS + """ bexpr : CODE bexpr %prec UMINUS """ def asc(x): @@ -2974,7 +3261,7 @@ def asc(x): def p_sgn(p): - """ expr : SGN expr %prec UMINUS + """ bexpr : SGN bexpr %prec UMINUS """ sgn = lambda x: x < 0 and -1 or x > 0 and 1 or 0 # noqa @@ -2989,94 +3276,50 @@ def p_sgn(p): # ---------------------------------------- -# Trigonometrics -# ---------------------------------------- -def p_expr_sin(p): - """ expr : SIN expr %prec UMINUS - """ - p[0] = make_builtin(p.lineno(1), 'SIN', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.sin(x)) - - -def p_expr_cos(p): - """ expr : COS expr %prec UMINUS - """ - p[0] = make_builtin(p.lineno(1), 'COS', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.cos(x)) - - -def p_expr_tan(p): - """ expr : TAN expr %prec UMINUS - """ - p[0] = make_builtin(p.lineno(1), 'TAN', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.tan(x)) - - -def p_expr_asin(p): - """ expr : ASN expr %prec UMINUS - """ - p[0] = make_builtin(p.lineno(1), 'ASN', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.asin(x)) - - -def p_expr_acos(p): - """ expr : ACS expr %prec UMINUS - """ - p[0] = make_builtin(p.lineno(1), 'ACS', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.acos(x)) - - -def p_expr_atan(p): - """ expr : ATN expr %prec UMINUS - """ - p[0] = make_builtin(p.lineno(1), 'ATN', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.atan(x)) - - -# ---------------------------------------- -# Square root, Exponent and logarithms +# Trigonometrics and LN, EXP, SQR # ---------------------------------------- -def p_expr_exp(p): - """ expr : EXP expr %prec UMINUS - """ - p[0] = make_builtin(p.lineno(1), 'EXP', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.exp(x)) - - -def p_expr_logn(p): - """ expr : LN expr %prec UMINUS +def p_expr_trig(p): + """ bexpr : math_fn bexpr %prec UMINUS """ - p[0] = make_builtin(p.lineno(1), 'LN', + p[0] = make_builtin(p.lineno(1), p[1], make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.log(x)) - - -def p_expr_sqrt(p): - """ expr : SQR expr %prec UMINUS + {'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]]) + + +def p_math_fn(p): + """ math_fn : SIN + | COS + | TAN + | ASN + | ACS + | ATN + | LN + | EXP + | SQR """ - p[0] = make_builtin(p.lineno(1), 'SQR', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.sqrt(x)) + p[0] = p[1] # ---------------------------------------- # Other important functions # ---------------------------------------- def p_expr_int(p): - """ expr : INT expr %prec UMINUS + """ bexpr : INT bexpr %prec UMINUS """ p[0] = make_typecast(TYPE.long_, p[2], p.lineno(1)) def p_abs(p): - """ expr : ABS expr %prec UMINUS + """ bexpr : ABS bexpr %prec UMINUS """ if is_unsigned(p[2]): p[0] = p[2] @@ -3085,6 +3328,8 @@ def p_abs(p): p[0] = make_builtin(p.lineno(1), 'ABS', p[2], lambda x: x if x >= 0 else -x) +# endregion + # ---------------------------------------- # The yyerror function diff --git a/zxbpp.py b/zxbpp.py index 12e30c1de..5aaf9819c 100755 --- a/zxbpp.py +++ b/zxbpp.py @@ -14,14 +14,17 @@ import sys import os import re +import argparse from zxbpplex import tokens # noqa import zxbpplex import zxbasmpplex from ply import yacc +import api from api.config import OPTIONS from api import global_ +import api.utils from prepro.output import warning, error, CURRENT_FILE from prepro import DefinesTable, ID, MacroCall, Arg, ArgList from prepro.exceptions import PreprocError @@ -71,11 +74,13 @@ def init(): global_.FILENAME = '(stdin)' OUTPUT = '' INCLUDED = {} - CURRENT_DIR = get_include_path() - INCLUDEPATH = ('library', 'library-asm') + CURRENT_DIR = '' + pwd = get_include_path() + INCLUDEPATH = [os.path.join(pwd, 'library'), os.path.join(pwd, 'library-asm')] ENABLED = True IFDEFS = [] global_.has_errors = 0 + global_.error_msg_cache.clear() parser.defaulted_states = {} ID_TABLE = DefinesTable() del CURRENT_FILE[:] @@ -89,21 +94,14 @@ def get_include_path(): f2 = os.path.basename(sys.executable).lower() # Executable filename # If executable filename and script name are the same, we are - if f1 == f2 or f2 == f1 + '.exe': # under a "compiled" py2exe binary + if f1 == f2 or f2 == f1 + '.exe': # under a "compiled" python binary result = os.path.dirname(os.path.realpath(sys.executable)) else: - result = os.path.dirname(os.path.realpath(sys.argv[0])) + result = os.path.dirname(os.path.realpath(__file__)) return result -def sanitize_file(fname): - """ Given a file name (string) returns it with back-slashes reversed. - This is to make all BASIC programs compatible in all OSes - """ - return fname.replace('\\', '/') - - def setMode(mode): global LEXER @@ -117,20 +115,20 @@ def setMode(mode): LEXER = zxbpplex.Lexer() -def search_filename(fname, lineno): - """ Search a filename into the list of the include path +def search_filename(fname, lineno, local_first): + """ 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. """ - fname = sanitize_file(fname) - include_path_dirs = OPTIONS.include_path.value.split(':') if OPTIONS.include_path.value else [] - - for i in INCLUDEPATH: - if not os.path.isabs(i): - for j in [CURRENT_DIR] + include_path_dirs + os.environ['PATH'].split(os.pathsep): - path = os.path.join(j, i, fname) - if os.path.exists(path): - return path - else: - path = os.path.join(i, fname) + fname = api.utils.sanitize_filename(fname) + i_path = [CURRENT_DIR] + INCLUDEPATH if local_first else list(INCLUDEPATH) + i_path.extend(OPTIONS.include_path.value.split(':') if OPTIONS.include_path.value else []) + if os.path.isabs(fname): + if os.path.isfile(fname): + return fname + else: + for dir_ in i_path: + path = api.utils.sanitize_filename(os.path.join(dir_, fname)) if os.path.exists(path): return path @@ -138,11 +136,17 @@ def search_filename(fname, lineno): return '' -def include_file(filename, lineno): - """ Writes down that "filename" was included at the current file, - at line +def include_file(filename, lineno, local_first): + """ Performs a file inclusion (#include) in the preprocessor. + Writes down that "filename" was included at the current file, + at line . + + 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 = sanitize_file(filename) + global CURRENT_DIR + filename = search_filename(filename, lineno, local_first) if filename not in INCLUDED.keys(): INCLUDED[filename] = [] @@ -150,15 +154,25 @@ def include_file(filename, lineno): INCLUDED[filename].append((CURRENT_FILE[-1], lineno)) CURRENT_FILE.append(filename) + CURRENT_DIR = os.path.dirname(filename) return LEXER.include(filename) -def include_once(filename, lineno): - """ Do as above only in file not already included +def include_once(filename, lineno, local_first): + """ Performs a file inclusion (#include) in the preprocessor. + Writes down that "filename" was included at the current file, + at line . + + The file is ignored if it was previuosly included (a warning will + 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 = sanitize_file(filename) + filename = search_filename(filename, lineno, local_first) if filename not in INCLUDED.keys(): # If not already included - return include_file(filename, lineno) # include it and return + return include_file(filename, lineno, local_first) # include it and return # Now checks if the file has been included more than once if len(INCLUDED[filename]) > 1: @@ -188,6 +202,8 @@ def p_program(p): | ifdef | require | pragma + | errormsg + | warningmsg """ p[0] = p[1] @@ -218,6 +234,8 @@ def p_program_char(p): | program ifdef | program require | program pragma + | program errormsg + | program warningmsg """ p[0] = p[1] + p[2] @@ -229,8 +247,10 @@ def p_program_newline(p): tmp = [str(x()) if isinstance(x, MacroCall) else x for x in p[2]] except PreprocError as v: error(v.lineno, v.message) + p[0] = [] + return - p[0] = p[1] # + tmp # + [p[3]] + p[0] = p[1] p[0].extend(tmp) p[0].append(p[3]) @@ -254,8 +274,10 @@ def p_token(p): def p_include_file(p): """ include_file : include NEWLINE program _ENDFILE_ """ + global CURRENT_DIR p[0] = [p[1] + p[2]] + p[3] + [p[4]] CURRENT_FILE.pop() # Remove top of the stack + CURRENT_DIR = os.path.dirname(CURRENT_FILE[-1]) def p_include_file_empty(p): @@ -273,15 +295,17 @@ def p_include_once_empty(p): def p_include_once_ok(p): """ include_file : include_once NEWLINE program _ENDFILE_ """ - p[0] = [p[1]] + p[3] + [p[4]] + global CURRENT_DIR + p[0] = [p[1] + p[2]] + p[3] + [p[4]] CURRENT_FILE.pop() # Remove top of the stack + CURRENT_DIR = os.path.dirname(CURRENT_FILE[-1]) def p_include(p): """ include : INCLUDE STRING """ if ENABLED: - p[0] = include_file(p[2], p.lineno(2)) + p[0] = include_file(p[2], p.lineno(2), local_first=True) else: p[0] = [] p.lexer.next_token = '_ENDFILE_' @@ -291,21 +315,17 @@ def p_include_fname(p): """ include : INCLUDE FILENAME """ if ENABLED: - l = p.lineno(2) - fname = search_filename(p[2], l) - if fname: - p[0] = include_file(search_filename(p[2], l), l) - return - - p[0] = [] - p.lexer.next_token = '_ENDFILE_' + p[0] = include_file(p[2], p.lineno(2), local_first=False) + else: + p[0] = [] + p.lexer.next_token = '_ENDFILE_' def p_include_once(p): """ include_once : INCLUDE ONCE STRING """ if ENABLED: - p[0] = include_once(p[3], p.lineno(3)) + p[0] = include_once(p[3], p.lineno(3), local_first=True) else: p[0] = [] @@ -319,10 +339,9 @@ def p_include_once_fname(p): p[0] = [] if ENABLED: - l = p.lineno(3) - fname = search_filename(p[3], l) - if fname: - p[0] = include_once(fname, l) + p[0] = include_once(p[3], p.lineno(3), local_first=False) + else: + p[0] = [] if not p[0]: p.lexer.next_token = '_ENDFILE_' @@ -349,7 +368,7 @@ def p_line_file(p): def p_require_file(p): """ require : REQUIRE STRING NEWLINE """ - p[0] = ['#%s "%s"\n' % (p[1], sanitize_file(p[2]))] + p[0] = ['#%s "%s"\n' % (p[1], api.utils.sanitize_filename(p[2]))] def p_init(p): @@ -368,6 +387,22 @@ def p_undef(p): p[0] = [] +def p_errormsg(p): + """ errormsg : ERROR STRING + """ + if ENABLED: + error(p.lineno(1), p[2]) + p[0] = [] + + +def p_warningmsg(p): + """ warningmsg : WARNING STRING + """ + if ENABLED: + warning(p.lineno(1), p[2]) + p[0] = [] + + def p_define(p): """ define : DEFINE ID params defs """ @@ -702,15 +737,20 @@ def filter_(input_, filename='', state='INITIAL'): """ Filter the input string thought the preprocessor. result is appended to OUTPUT global str """ + global CURRENT_DIR + + prev_dir = CURRENT_DIR CURRENT_FILE.append(filename) + CURRENT_DIR = os.path.dirname(CURRENT_FILE[-1]) LEXER.input(input_, filename) LEXER.lex.begin(state) parser.parse(lexer=LEXER, debug=OPTIONS.Debug.value > 2) CURRENT_FILE.pop() + CURRENT_DIR = prev_dir def main(argv): - global OUTPUT, ID_TABLE, ENABLED + global OUTPUT, ID_TABLE, ENABLED, CURRENT_DIR ENABLED = True OUTPUT = '' @@ -719,18 +759,20 @@ def main(argv): CURRENT_FILE.append(argv[0]) else: CURRENT_FILE.append(global_.FILENAME) + CURRENT_DIR = os.path.dirname(CURRENT_FILE[-1]) if OPTIONS.Sinclair.value: - included_file = search_filename('sinclair.bas', 0) + included_file = search_filename('sinclair.bas', 0, local_first=False) if not included_file: return - OUTPUT += include_once(included_file, 0) + OUTPUT += include_once(included_file, 0, local_first=False) if len(OUTPUT) and OUTPUT[-1] != '\n': OUTPUT += '\n' parser.parse(lexer=LEXER, debug=OPTIONS.Debug.value > 2) CURRENT_FILE.pop() + CURRENT_DIR = os.path.dirname(CURRENT_FILE[-1]) prev_file = global_.FILENAME global_.FILENAME = CURRENT_FILE[-1] @@ -751,11 +793,38 @@ def main(argv): # ------- ERROR And Warning messages ---------------- -def entry_point(): +def entry_point(args=None): + if args is None: + args = sys.argv[1:] + + api.config.init() init() - result = main(sys.argv[1:]) + setMode('BASIC') + + parser = argparse.ArgumentParser() + parser.add_argument('-o', '--output', type=str, dest='output_file', default=None, + help='Sets output file. If not specified, will output to console (STDOUT)') + parser.add_argument('-d', '--debug', dest='debug', default=OPTIONS.Debug.value, action='count', + help='Enable verbosity/debugging output. Additional -d increase verbosity/debug level') + parser.add_argument('-e', '--errmsg', type=str, dest='stderr', default=None, + help='Error messages file (standard error console by default)') + parser.add_argument('input_file', type=str, default=None, nargs='?', + help="File to parse. If not specified, console input will be used (STDIN)") + + options = parser.parse_args(args=args) + OPTIONS.Debug.value = options.debug + + if options.stderr: + OPTIONS.StdErrFileName.value = options.stderr + OPTIONS.stderr.value = api.utils.open_file(OPTIONS.StdErrFileName.value, 'wt', 'utf-8') + + result = main([options.input_file] if options.input_file else []) if not global_.has_errors: # ok? - OPTIONS.stdout.value.write(OUTPUT) + if options.output_file: + with api.utils.open_file(options.output_file, 'wt', 'utf-8') as output_file: + output_file.write(OUTPUT) + else: + OPTIONS.stdout.value.write(OUTPUT) return result diff --git a/zxbpplex.py b/zxbpplex.py index f2e017d4a..f829bb964 100755 --- a/zxbpplex.py +++ b/zxbpplex.py @@ -10,7 +10,6 @@ # This is the Lexer for the ZXBpp (ZXBasic Preprocessor) # ---------------------------------------------------------------------- -import os import sys from ply import lex from prepro.output import warning, error @@ -34,7 +33,8 @@ ('comment', 'exclusive'), ('asm', 'exclusive'), ('asmcomment', 'exclusive'), - ('if', 'exclusive') + ('if', 'exclusive'), + ('msg', 'exclusive') ) _tokens = ('STRING', 'TOKEN', 'NEWLINE', '_ENDFILE_', 'FILENAME', 'ID', @@ -56,6 +56,8 @@ 'line': 'LINE', 'require': 'REQUIRE', 'pragma': 'PRAGMA', + 'error': 'ERROR', + 'warning': 'WARNING' } # List of token names. @@ -85,8 +87,9 @@ def t_asm_asmEnd(self, t): return t def t_asm_CONTINUE(self, t): - r'[\\_]([ \t]*;.*)?\r?\n' # TODO: remove _ from line continuation in ASM contexts + r'[\\_]\r?\n' t.lexer.lineno += 1 + t.value = t.value[1:] return t def t_asm_COMMENT(self, t): @@ -114,12 +117,12 @@ def t_asm_ID(self, t): return t def t_asm_CHAR(self, t): - r"'([^'\n]|'')'" + r"'[^'\n]|'''" t.type = 'TOKEN' return t def t_asm_TOKEN(self, t): - r"[][',.:$()*/+-]" + r"[]['.:$*/+<>|&~%^-]" return t def t_INITIAL_CONTINUE(self, t): @@ -173,6 +176,13 @@ def t_comment_endBlock(self, t): if not self.__COMMENT_LEVEL: t.lexer.begin('INITIAL') + def t_msg_STRING(self, t): + r'.*\n' + t.lexer.lineno += 1 + t.lexer.begin('INITIAL') + t.value = t.value.strip() # remove newline an spaces + return t + # Any other character is ignored until EOL def t_singlecomment_comment_Skip(self, t): r'.' @@ -214,16 +224,17 @@ def t_if_LT(self, t): def t_prepro_ID(self, t): r'[_a-zA-Z][_a-zA-Z0-9]*' # preprocessor directives t.type = reserved_directives.get(t.value.lower(), 'ID') - if t.type == 'DEFINE': - t.lexer.begin('define') - - if t.type == 'PRAGMA': - t.lexer.begin('pragma') - - if t.type == 'IF': - t.lexer.begin('if') - - if t.type == 'ID' and self.expectingDirective: + states_ = { + 'DEFINE': 'define', + 'PRAGMA': 'pragma', + 'IF': 'if', + 'ERROR': 'msg', + 'WARNING': 'msg' + } + + if t.type in states_: + t.lexer.begin(states_[t.type]) + elif t.type == 'ID' and self.expectingDirective: self.error("invalid directive #%s" % t.value) self.expectingDirective = False @@ -237,11 +248,11 @@ def t_pragma_RP(self, t): r'\)' return t - def t_INITIAL_defexpr_if_LLP(self, t): + def t_INITIAL_asm_defexpr_if_LLP(self, t): r'\(' return t - def t_INITIAL_defexpr_if_RRP(self, t): + def t_INITIAL_asm_defexpr_if_RRP(self, t): r'\)' return t @@ -311,7 +322,7 @@ def t_pragma_EQ(self, t): r'=' return t - def t_INITIAL_defexpr_defargs_prepro_COMMA(self, t): + def t_INITIAL_asm_defexpr_defargs_prepro_COMMA(self, t): r',' return t @@ -353,16 +364,28 @@ def t_prepro_FILENAME(self, t): t.value = t.value[1:-1] # Remove quotes return t - def t_INITIAL_defargs_defargsopt_prepro_define_defexpr_pragma_comment_singlecomment_asm_asmcomment_if_error(self, - t): - """ error handling rule + def t_INITIAL_defargs_defargsopt_prepro_define_defexpr_pragma_comment_singlecomment_ANY(self, t): + r'.' + self.error("illegal preprocessor character '%s'" % t.value[0]) + + def t_INITIAL_defargs_defargsopt_prepro_define_defexpr_pragma_comment_singlecomment_error(self, t): + """ error handling rule. Should never happen! """ + pass # The lexer will raise an exception here. This is intended + + def t_asm_asmcomment_if_ANY(self, t): + r'.' self.error("illegal preprocessor character '%s'" % t.value[0]) + def t_asm_asmcomment_if_msg_error(self, t): + """ error handling rule. Should never happen! + """ + pass + def put_current_line(self, prefix='', suffix=''): """ Returns line and file for include / end of include sequences. """ - return '%s#line %i "%s"%s' % (prefix, self.lex.lineno, os.path.basename(self.filestack[-1][0]), suffix) + return '%s#line %i "%s"%s' % (prefix, self.lex.lineno, self.filestack[-1][0], suffix) def include(self, filename): """ Changes FILENAME and line count @@ -387,11 +410,10 @@ def include(self, filename): self.input_data = api.utils.read_txt_file(filename) if len(self.input_data) and self.input_data[-1] != EOL: self.input_data += EOL - - self.lex.input(self.input_data) except IOError: - self.error('cannot open "%s" file' % filename) + self.input_data = EOL + self.lex.input(self.input_data) return result def include_end(self): @@ -467,10 +489,9 @@ def find_column(self, token): return column def error(self, msg): - """ Prints an error msg, and exits. + """ Prints an error msg and continues execution. """ error(self.lex.lineno, msg) - sys.exit(1) def warning(self, msg): """ Emits a warning and continue execution.