diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 000000000..cc84ba2f3 --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,4 @@ +[bumpversion] +current_version = 1.5.0 +files = version.py + diff --git a/.gitignore b/.gitignore index eeb6ee339..796f08aab 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ parsetab.py parser.out zxbasmtab.py zxbpptab.py -.idea \ No newline at end of file +.idea +/zxbtab.py diff --git a/api/check.py b/api/check.py index 3f69cac42..3be5aab65 100644 --- a/api/check.py +++ b/api/check.py @@ -9,11 +9,11 @@ # the GNU General License # ---------------------------------------------------------------------- -import config -import global_ -from constants import CLASS -from constants import SCOPE -from errmsg import syntax_error +from . import config +from . import global_ +from .constants import CLASS +from .constants import SCOPE +from .errmsg import syntax_error __all__ = ['check_type', diff --git a/api/config.py b/api/config.py index 4380eea4a..4c7bb1e41 100644 --- a/api/config.py +++ b/api/config.py @@ -16,8 +16,8 @@ import sys # The options container -import options -import global_ +from . import options +from . import global_ OPTIONS = options.Options() diff --git a/api/constants.py b/api/constants.py index 1c983b180..4104bbc24 100644 --- a/api/constants.py +++ b/api/constants.py @@ -9,14 +9,14 @@ # the GNU General License # ---------------------------------------------------------------------- +from .decorator import classproperty + __all__ = [ 'ID_CLASSES', 'DEPRECATED_SUFFIXES', 'ID_TYPES', 'TYPE_NAMES', 'NAME_TYPES', 'TYPE_SIZES', 'SUFFIX_TYPE', 'PTR_TYPE' ] -from decorator import classproperty - # ------------------------------------------------- # Global constants # ------------------------------------------------- diff --git a/api/debug.py b/api/debug.py index 4c425125f..6c2921942 100644 --- a/api/debug.py +++ b/api/debug.py @@ -5,16 +5,15 @@ # Simple debugging module import os +import inspect -from config import OPTIONS +from .config import OPTIONS __all__ = ['__DEBUG__', '__LINE__', '__FILE__'] # --------------------- END OF GLOBAL FLAGS --------------------- -import inspect - -def __DEBUG__(msg, level = 1): +def __DEBUG__(msg, level=1): if level > OPTIONS.Debug.value: return diff --git a/api/errmsg.py b/api/errmsg.py index 55e7a239d..06fbcfc8b 100644 --- a/api/errmsg.py +++ b/api/errmsg.py @@ -10,8 +10,8 @@ # ---------------------------------------------------------------------- import sys -import global_ -from config import OPTIONS +from . import global_ +from .config import OPTIONS # Expors only these functions. Others __all__ = ['syntax_error', 'warning'] diff --git a/api/global_.py b/api/global_.py index 6f84b6e3d..4d7704a00 100644 --- a/api/global_.py +++ b/api/global_.py @@ -17,8 +17,8 @@ # Don't touch unless you know what are you doing # ---------------------------------------------------------------------- -from opcodestemps import OpcodesTemps -from constants import TYPE +from .opcodestemps import OpcodesTemps +from .constants import TYPE # ---------------------------------------------------------------------- # Initializes a singleton container diff --git a/api/optimize.py b/api/optimize.py index 5680381cd..80a567258 100644 --- a/api/optimize.py +++ b/api/optimize.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from ast_ import NodeVisitor -from config import OPTIONS +from .config import OPTIONS from api.errmsg import warning import api.check as chk from api.constants import TYPE diff --git a/api/options.py b/api/options.py index 622a8f1bc..3cbded70f 100644 --- a/api/options.py +++ b/api/options.py @@ -9,10 +9,11 @@ # the GNU General License # ---------------------------------------------------------------------- +from .errors import Error + TRUE = true = True FALSE = false = False -from errors import Error __all__ = ['Option', 'Options'] diff --git a/api/symboltable.py b/api/symboltable.py index d9ee3419c..97757b377 100644 --- a/api/symboltable.py +++ b/api/symboltable.py @@ -11,28 +11,27 @@ from collections import OrderedDict -from debug import __DEBUG__ +from .debug import __DEBUG__ import symbols - from symbols.symbol_ import Symbol -import global_ -from config import OPTIONS +from . import global_ +from .config import OPTIONS -from errmsg import syntax_error -from errmsg import warning_implicit_type -from errmsg import syntax_error_func_type_mismatch -from errmsg import syntax_error_not_array_nor_func +from .errmsg import syntax_error +from .errmsg import warning_implicit_type +from .errmsg import syntax_error_func_type_mismatch +from .errmsg import syntax_error_not_array_nor_func -from constants import DEPRECATED_SUFFIXES -from constants import SUFFIX_TYPE -from constants import SCOPE -from constants import CLASS -from constants import TYPE +from .constants import DEPRECATED_SUFFIXES +from .constants import SUFFIX_TYPE +from .constants import SCOPE +from .constants import CLASS +from .constants import TYPE -from check import is_number -from check import check_is_declared_strict +from .check import is_number +from .check import check_is_declared_strict # ---------------------------------------------------------------------- diff --git a/arch/__init__.py b/arch/__init__.py index 87c40daf5..1454dd7bf 100755 --- a/arch/__init__.py +++ b/arch/__init__.py @@ -2,4 +2,4 @@ # -*- coding: utf-8 -*- # vim:ts=4:et:sw=4: -import zx48k +from . import zx48k diff --git a/arch/zx48k/__init__.py b/arch/zx48k/__init__.py index e5b6d43d8..02317f64d 100755 --- a/arch/zx48k/__init__.py +++ b/arch/zx48k/__init__.py @@ -2,8 +2,8 @@ # -*- coding: utf-8 -*- # vim:ts=4:et:sw=4: -import beep -from translator import * +from . import beep +from .translator import * import api.global_ from api.constants import TYPE diff --git a/arch/zx48k/beep.py b/arch/zx48k/beep.py index 9e182088d..01818ee99 100755 --- a/arch/zx48k/beep.py +++ b/arch/zx48k/beep.py @@ -2,65 +2,67 @@ # -*- coding: utf-8 -*- # vim:ts=4:et:sw=4: -''' This library converts duration,pitch for a beep +__doc___ = """This library converts duration,pitch for a beep from floating point to HL,DE Integers. -''' +""" + + class BeepError(BaseException): - ''' Returned when invalid pitch specified (e.g. Out of Range) - ''' - def __init__(self, msg = 'Invalid beep parameters'): + """Returned when invalid pitch specified (e.g. Out of Range) + """ + + def __init__(self, msg='Invalid beep parameters'): self.message = msg def __str__(self): return self.message - + + # Pitch (frequencies) tables -TABLE = [261.625565290, # C - 277.182631135, - 293.664768100, - 311.126983881, - 329.627557039, - 349.228231549, - 369.994422674, - 391.995436072, - 415.304697513, - 440.000000000, - 466.163761616, - 493.883301378] +TABLE = [261.625565290, # C + 277.182631135, + 293.664768100, + 311.126983881, + 329.627557039, + 349.228231549, + 369.994422674, + 391.995436072, + 415.304697513, + 440.000000000, + 466.163761616, + 493.883301378] def getDEHL(duration, pitch): - ''' Converts duration,pitch to a pair of unsigned 16 bit integers, + """Converts duration,pitch to a pair of unsigned 16 bit integers, to be loaded in DE,HL, following the ROM listing. Returns a t-uple with the DE, HL values. - ''' + """ intPitch = int(pitch) - fractPitch = pitch - intPitch # Gets fractional part + fractPitch = pitch - intPitch # Gets fractional part tmp = 1 + 0.0577622606 * fractPitch if not -60 <= intPitch <= 127: raise BeepError('Pitch out of range: must be between [-60, 127]') if duration < 0 or duration > 10: raise BeepError('Invalid duration: must be between [0, 10]') - + A = intPitch + 60 - B = -5 + int(A / 12) # -5 <= B <= 10 - A %= 0xC # Semitones above C - + B = -5 + int(A / 12) # -5 <= B <= 10 + A %= 0xC # Semitones above C + frec = TABLE[A] tmp2 = tmp * frec f = tmp2 * 2.0 ** B - - DE = int (0.5 + f * duration - 1) - HL = int (0.5 + 437500.0 / f - 30.125) - return (DE, HL) - + DE = int(0.5 + f * duration - 1) + HL = int(0.5 + 437500.0 / f - 30.125) + return DE, HL + if __name__ == '__main__': # Simple test - print getDEHL(1, 0), [hex(x) for x in getDEHL(1, 0)] - print getDEHL(5, 0), [hex(x) for x in getDEHL(5, 0)] - print getDEHL(1.5, 15.0), [hex(x) for x in getDEHL(1.5, 15.0)] - print getDEHL(1.5, 17.0), [hex(x) for x in getDEHL(1.5, 17.0)] - + print(getDEHL(1, 0), [hex(x) for x in getDEHL(1, 0)]) + print(getDEHL(5, 0), [hex(x) for x in getDEHL(5, 0)]) + print(getDEHL(1.5, 15.0), [hex(x) for x in getDEHL(1.5, 15.0)]) + print(getDEHL(1.5, 17.0), [hex(x) for x in getDEHL(1.5, 17.0)]) diff --git a/arch/zx48k/translator.py b/arch/zx48k/translator.py index e983c8938..9db6f5161 100644 --- a/arch/zx48k/translator.py +++ b/arch/zx48k/translator.py @@ -1,9 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -__all__ = ['Translator', - 'VarTranslator', - 'FunctionTranslator'] +import functools +from collections import OrderedDict from api.constants import TYPE from api.constants import SCOPE @@ -35,13 +34,17 @@ import symbols from symbols.type_ import Type -import arch.zx48k #TODO: put this in global +import arch.zx48k # TODO: put this in global + +__all__ = ['Translator', + 'VarTranslator', + 'FunctionTranslator'] class TranslatorVisitor(NodeVisitor): - ''' This visitor just adds the emit() method. - ''' - STRING_LABELS = {} + """ This visitor just adds the emit() method. + """ + STRING_LABELS = OrderedDict() # ------------------------------------------------ # A list of tokens that belongs to temporary # ATTR setting @@ -67,11 +70,11 @@ def O_LEVEL(self): @staticmethod def TYPE(type_): - ''' Converts a backend type (from api.constants) + """ Converts a backend type (from api.constants) to a SymbolTYPE object (taken from the SYMBOL_TABLE). If type_ is already a SymbolTYPE object, nothing is done. - ''' + """ if isinstance(type_, symbols.TYPE): return type_ @@ -108,8 +111,8 @@ def emit(*args): @staticmethod def dumpMemory(MEMORY): - ''' Returns a sequence of Quads - ''' + """ Returns a sequence of Quads + """ for x in MEMORY: yield str(x) @@ -123,7 +126,7 @@ def visit_BLOCK(self, node): def visit_ATTR_TMP(self, node): yield node.children[0] self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) - self.emit('call', node.token, 0) # Procedure call. Discard return + self.emit('call', node.token, 0) # Procedure call. Discard return ifile = node.token.lower() ifile = ifile[:ifile.index('_')] backend.REQUIRES.add(ifile + '.asm') @@ -143,8 +146,8 @@ def _visit(self, node): return NodeVisitor._visit(self, node) def norm_attr(self): - ''' Normalize attr state - ''' + """ Normalize attr state + """ if not self.HAS_ATTR: return @@ -154,9 +157,9 @@ def norm_attr(self): @staticmethod def traverse_const(node): - ''' Traverses a constant and returns an string + """ Traverses a constant and returns an string with the arithmetic expression - ''' + """ if node.token == 'NUMBER': return node.t @@ -202,31 +205,29 @@ def traverse_const(node): @staticmethod def check_attr(node, n): - ''' Check if ATTR has to be normalized + """ Check if ATTR has to be normalized after this instruction has been translated to intermediate code. - ''' + """ if len(node.children) > n: return node.children[n] class Translator(TranslatorVisitor): - ''' ZX Spectrum translator - ''' + """ ZX Spectrum translator + """ + def visit_NOP(self, node): pass # nothing to do - def visit_CLS(self, node): self.emit('call', 'CLS', 0) backend.REQUIRES.add('cls.asm') - def visit_NUMBER(self, node): __DEBUG__('NUMBER ' + str(node)) yield node.value - def visit_STRING(self, node): __DEBUG__('STRING ' + str(node)) if self.STRING_LABELS.get(node.value, None) is None: @@ -235,13 +236,11 @@ def visit_STRING(self, node): node.t = '#' + self.STRING_LABELS[node.value] yield node.t - def visit_END(self, node): arg = (yield node.children[0]) __DEBUG__('END') self.emit('end', arg) - def visit_ERROR(self, node): # Raises an error yield node.children[0] @@ -249,17 +248,15 @@ def visit_ERROR(self, node): self.emit('call', '__ERROR', 0) backend.REQUIRES.add('error.asm') - def visit_STOP(self, node): - ''' Returns to BASIC with an error code - ''' + """ Returns to BASIC with an error code + """ yield node.children[0] self.emit('fparamu8', node.children[0].t) self.emit('call', '__STOP', 0) self.emit('end', 0) backend.REQUIRES.add('error.asm') - def visit_LET(self, node): assert isinstance(node.children[0], symbols.VAR) if self.O_LEVEL < 2 or node.children[0].accessed or node.children[1].token == 'CONST': @@ -267,7 +264,6 @@ def visit_LET(self, node): __DEBUG__('LET') self.emit_let_left_part(node) - def visit_POKE(self, node): ch0 = node.children[0] ch1 = node.children[1] @@ -279,7 +275,6 @@ def visit_POKE(self, node): else: self.emit('store' + self.TSUFFIX(ch1.type_), ch0.t, ch1.t) - def visit_RANDOMIZE(self, node): yield node.children[0] self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) @@ -291,7 +286,6 @@ def visit_LABEL(self, node): for tmp in node.aliased_by: self.emit('label', tmp.mangled) - def visit_VAR(self, node): __DEBUG__('{}: VAR {}:{} Scope: {} Class: {}'.format(node.lineno, node.name, node.type_, SCOPE.to_string(node.scope), CLASS.to_string(node.class_))) @@ -329,7 +323,6 @@ def visit_PARAMDECL(self, node): assert node.scope == SCOPE.parameter self.visit_VAR(node) - def visit_UNARY(self, node): uvisitor = UnaryOpTranslator() att = 'visit_{}'.format(node.operator) @@ -339,7 +332,6 @@ def visit_UNARY(self, node): raise InvalidOperatorError(node.operator) - def visit_BUILTIN(self, node): yield node.operand bvisitor = BuiltinTranslator() @@ -350,7 +342,6 @@ def visit_BUILTIN(self, node): raise InvalidBuiltinFunctionError(node.fname) - def visit_BINARY(self, node): yield node.left yield node.right @@ -359,7 +350,6 @@ def visit_BINARY(self, node): s = self.TSUFFIX(node.left.type_) # Operands type self.emit(ins + s, node.t, str(node.left.t), str(node.right.t)) - def visit_TYPECAST(self, node): yield node.operand assert node.operand.type_.is_basic @@ -367,12 +357,10 @@ def visit_TYPECAST(self, node): self.emit('cast', node.t, self.TSUFFIX(node.operand.type_.type_), self.TSUFFIX(node.type_.type_), node.operand.t) - def visit_FUNCDECL(self, node): # Delay emission of functions until the end of the main code gl.FUNCTIONS.append(node.entry) - def visit_CALL(self, node): yield node.args # arglist if node.entry.convention == CONVENTION.fastcall: @@ -384,19 +372,17 @@ def visit_CALL(self, node): self.emit('call', '__MEM_FREE', 0) # Discard string return value if the called function has any backend.REQUIRES.add('free.asm') - def visit_ARGLIST(self, node): for i in range(len(node) - 1, -1, -1): # visit in reverse order yield node[i] - def visit_ARGUMENT(self, node): if not node.byref: suffix = self.TSUFFIX(node.type_) if node.value.token in ('VAR', 'PARAMDECL') and \ node.type_.is_dynamic and node.value.t[0] == '$': # Duplicate it in the heap - assert(node.value.scope in (SCOPE.local, SCOPE.parameter)) + assert (node.value.scope in (SCOPE.local, SCOPE.parameter)) if node.value.scope == SCOPE.local: self.emit('pload' + suffix, node.t, str(-node.value.offset)) else: # PARAMETER @@ -423,7 +409,6 @@ def visit_ARGUMENT(self, node): self.emit('paramu16', t) - def visit_ARRAYLOAD(self, node): scope = node.entry.scope @@ -450,7 +435,6 @@ def visit_ARRAYLOAD(self, node): elif scope == SCOPE.local: self.emit('pload' + self.TSUFFIX(node.type_), node.t, -(node.entry.offset - offset)) - def visit_ARRAYCOPY(self, node): tr = node.children[0] scope = tr.scope @@ -485,7 +469,6 @@ def visit_ARRAYCOPY(self, node): self.emit('call', 'STR_ARRAYCOPY', 0) backend.REQUIRES.add('strarraycpy.asm') - def visit_LETARRAY(self, node): if self.O_LEVEL > 1 and not node.children[0].entry.accessed: return @@ -513,7 +496,6 @@ def visit_LETARRAY(self, node): elif scope == SCOPE.local: self.emit('pstore' + suf, -(arr.entry.offset - arr.offset), node.children[1].t) - def visit_LETSUBSTR(self, node): yield node.children[3] self.emit('paramstr', node.children[3].t) @@ -533,18 +515,16 @@ def visit_LETSUBSTR(self, node): 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 \ - node.string.token == 'VAR' and node.string.scope == SCOPE.global_: + node.string.token == 'VAR' and node.string.scope == SCOPE.global_: self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.string.t) # Now emit the slicing indexes @@ -555,7 +535,7 @@ def visit_STRSLICE(self, node): self.emit('param' + self.TSUFFIX(node.upper.type_), node.upper.t) if node.string.token in ('VAR', 'PARAMDECL') and node.string.mangled[0] == '_' or \ - node.string.token == 'STRING': + node.string.token == 'STRING': self.emit('fparamu8', 0) else: self.emit('fparamu8', 1) # If the argument is not a variable, it must be freed @@ -563,7 +543,6 @@ def visit_STRSLICE(self, node): self.emit('call', '__STRSLICE', self.TYPE(gl.PTR_TYPE).size) backend.REQUIRES.add('strslice.asm') - def visit_FUNCCALL(self, node): yield node.args @@ -573,14 +552,14 @@ def visit_FUNCCALL(self, node): self.emit('call', node.entry.mangled, node.entry.size) - #region Control Flow Sentences + # region Control Flow Sentences # ----------------------------------------------------------------------------------------------------- # Control Flow Compound sentences FOR, IF, WHILE, DO UNTIL... # ----------------------------------------------------------------------------------------------------- def visit_DO_LOOP(self, node): loop_label = backend.tmp_label() end_loop = backend.tmp_label() - self.LOOPS.append(('DO', end_loop, loop_label)) # Saves which labels to jump upon EXIT or CONTINUE + self.LOOPS.append(('DO', end_loop, loop_label)) # Saves which labels to jump upon EXIT or CONTINUE self.emit('label', loop_label) if node.children: @@ -589,13 +568,11 @@ def visit_DO_LOOP(self, node): self.emit('jump', loop_label) self.emit('label', end_loop) self.LOOPS.pop() - #del loop_label, end_loop - + # del loop_label, end_loop def visit_DO_UNTIL(self, node): return self.visit_UNTIL_DO(node) - def visit_DO_WHILE(self, node): loop_label = backend.tmp_label() end_loop = backend.tmp_label() @@ -615,9 +592,7 @@ def visit_DO_WHILE(self, node): self.emit('jnzero' + self.TSUFFIX(node.children[0].type_), node.children[0].t, loop_label) self.emit('label', end_loop) self.LOOPS.pop() - #del loop_label, end_loop, continue_loop - - + # del loop_label, end_loop, continue_loop def visit_EXIT_DO(self, node): self.emit('jump', self.loop_exit_label('DO')) @@ -647,7 +622,7 @@ def visit_FOR(self, node): self.LOOPS.append(('FOR', end_loop, loop_continue)) # Saves which label to jump upon EXIT FOR and CONTINUE FOR - yield node.children[1] # Gets starting value (lower limit) + yield node.children[1] # Gets starting value (lower limit) self.emit_let_left_part(node) # Stores it in the iterator variable self.emit('jump', loop_label_start) @@ -677,7 +652,7 @@ def visit_FOR(self, node): self.emit('jgezero' + suffix, node.children[3].t, loop_label_gt) if not direct or node.children[3].value < 0: # Here for negative steps - # Compares if var < limit2 + # Compares if var < limit2 yield node.children[0] # Value of var yield node.children[2] # Value of limit2 self.emit('lt' + suffix, node.t, node.children[0].t, node.children[2].t) @@ -687,8 +662,8 @@ def visit_FOR(self, node): self.emit('jump', end_loop) self.emit('label', loop_label_gt) - if not direct or node.children[3].value >= 0 : # Here for positive steps - # Compares if var > limit2 + if not direct or node.children[3].value >= 0: # Here for positive steps + # Compares if var > limit2 yield node.children[0] # Value of var yield node.children[2] # Value of limit2 self.emit('gt' + suffix, node.t, node.children[0].t, node.children[2].t) @@ -696,24 +671,20 @@ 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 - + # 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) - def visit_GOSUB(self, node): self.emit('call', node.children[0].mangled, 0) - def visit_CHKBREAK(self, node): if self.PREV_TOKEN != node.token: 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): yield node.children[0] if_label_else = backend.tmp_label() @@ -733,18 +704,16 @@ def visit_IF(self, node): self.emit('label', if_label_endif) - def visit_RETURN(self, node): if len(node.children) == 2: # Something to return? yield node.children[1] self.emit('ret' + self.TSUFFIX(node.children[1].type_), node.children[1].t, - '%s__leave' % node.children[0].mangled) + '%s__leave' % node.children[0].mangled) elif len(node.children) == 1: self.emit('ret', '%s__leave' % node.children[0].mangled) else: self.emit('leave', '__fastcall__') - def visit_UNTIL_DO(self, node): loop_label = backend.tmp_label() end_loop = backend.tmp_label() @@ -764,8 +733,7 @@ def visit_UNTIL_DO(self, node): self.emit('jzero' + self.TSUFFIX(node.children[0].type_), node.children[0].t, loop_label) self.emit('label', end_loop) self.LOOPS.pop() - #del loop_label, end_loop, continue_loop - + # del loop_label, end_loop, continue_loop def visit_WHILE(self, node): loop_label = backend.tmp_label() @@ -783,12 +751,12 @@ def visit_WHILE(self, node): self.emit('label', end_loop) self.LOOPS.pop() - def visit_WHILE_DO(self, node): return self.visit_DO_WHILE(node) - #endregion - #region [Drawing Primitives] + # endregion + + # region [Drawing Primitives] # ----------------------------------------------------------------------------------------------------- # Drawing Primitives PLOT, DRAW, DRAW3, CIRCLE # ----------------------------------------------------------------------------------------------------- @@ -803,7 +771,6 @@ def visit_PLOT(self, node): backend.REQUIRES.add('plot.asm') self.HAS_ATTR = (TMP_HAS_ATTR is not None) - def visit_DRAW(self, node): TMP_HAS_ATTR = self.check_attr(node, 2) yield TMP_HAS_ATTR @@ -815,7 +782,6 @@ def visit_DRAW(self, node): backend.REQUIRES.add('draw.asm') self.HAS_ATTR = (TMP_HAS_ATTR is not None) - def visit_DRAW3(self, node): TMP_HAS_ATTR = self.check_attr(node, 3) yield TMP_HAS_ATTR @@ -829,7 +795,6 @@ def visit_DRAW3(self, node): backend.REQUIRES.add('draw3.asm') self.HAS_ATTR = (TMP_HAS_ATTR is not None) - def visit_CIRCLE(self, node): TMP_HAS_ATTR = self.check_attr(node, 3) yield TMP_HAS_ATTR @@ -843,9 +808,9 @@ def visit_CIRCLE(self, node): backend.REQUIRES.add('circle.asm') self.HAS_ATTR = (TMP_HAS_ATTR is not None) - #endregion + # endregion - #region [I/O Statements] + # region [I/O Statements] # ----------------------------------------------------------------------------------------------------- # PRINT, LOAD, SAVE and I/O statements # ----------------------------------------------------------------------------------------------------- @@ -854,7 +819,6 @@ def visit_OUT(self, node): yield node.children[1] self.emit('out', node.children[0].t, node.children[1].t) - def visit_PRINT(self, node): for i in node.children: yield i @@ -882,7 +846,6 @@ def visit_PRINT(self, node): else: self.norm_attr() - def visit_PRINT_AT(self, node): yield node.children[0] self.emit('paramu8', node.children[0].t) @@ -891,19 +854,16 @@ def visit_PRINT_AT(self, node): self.emit('call', 'PRINT_AT', 0) # Procedure call. Discard return backend.REQUIRES.add('print.asm') - def visit_PRINT_TAB(self, node): yield node.children[0] self.emit('fparamu8', node.children[0].t) self.emit('call', 'PRINT_TAB', 0) backend.REQUIRES.add('print.asm') - def visit_PRINT_COMMA(self, node): self.emit('call', 'PRINT_COMMA', 0) backend.REQUIRES.add('print.asm') - def visit_LOAD(self, node): yield node.children[0] self.emit('paramstr', node.children[0].t) @@ -916,7 +876,6 @@ def visit_LOAD(self, node): self.emit('call', 'LOAD_CODE', 0) backend.REQUIRES.add('load.asm') - def visit_SAVE(self, node): yield (node.children[0]) self.emit('paramstr', node.children[0].t) @@ -927,43 +886,39 @@ def visit_SAVE(self, node): self.emit('call', 'SAVE_CODE', 0) backend.REQUIRES.add('save.asm') - def visit_VERIFY(self, node): return self.visit_LOAD(node) - def visit_BORDER(self, node): yield node.children[0] self.emit('fparamu8', node.children[0].t) - self.emit('call', 'BORDER', 0) # Procedure call. Discard return + self.emit('call', 'BORDER', 0) # Procedure call. Discard return backend.REQUIRES.add('border.asm') - def visit_BEEP(self, node): if node.children[0].token == node.children[1].token == 'NUMBER': # BEEP , DE, HL = arch.zx48k.beep.getDEHL(float(node.children[0].t), float(node.children[1].t)) self.emit('paramu16', HL) self.emit('fparamu16', DE) - self.emit('call', '__BEEPER', 0) # Procedure call. Discard return + self.emit('call', '__BEEPER', 0) # Procedure call. Discard return backend.REQUIRES.add('beeper.asm') else: yield node.children[1] self.emit('paramf', node.children[1].t) yield node.children[0] self.emit('fparamf', node.children[0].t) - self.emit('call', 'BEEP', 0) # Procedure call. Discard return + self.emit('call', 'BEEP', 0) # Procedure call. Discard return backend.REQUIRES.add('beep.asm') - def visit_PAUSE(self, node): yield node.children[0] self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) self.emit('call', '__PAUSE', 0) backend.REQUIRES.add('pause.asm') - #endregion + # endregion - #region [ATTR Sentences] + # region [ATTR Sentences] # ----------------------------------------------------------------------- # ATTR sentences: INK, PAPER, BRIGHT, FLASH, INVERSE, OVER, ITALIC, BOLD # ----------------------------------------------------------------------- @@ -998,17 +953,18 @@ def visit_BOLD(self, node): def visit_ITALIC(self, node): return self.visit_ATTR_sentence(node) - #endregion + # endregion - #region [Other Sentences] + # region [Other Sentences] # ----------------------------------------------------------------------------------------------------- # Other Sentences, like ASM, etc.. # ----------------------------------------------------------------------------------------------------- def visit_ASM(self, node): self.emit('inline', node.asm, node.lineno) - #endregion - #region [Helpers] + # endregion + + # region [Helpers] # -------------------------------------- # Helpers # -------------------------------------- @@ -1035,38 +991,36 @@ 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) - #endregion + # endregion - #region [Static Methods] + # region [Static Methods] # -------------------------------------- # Static Methods # -------------------------------------- def loop_exit_label(self, loop_type): - ''' Returns the label for the given loop type which + """ Returns the label for the given loop type which exits the loop. loop_type must be one of 'FOR', 'WHILE', 'DO' - ''' + """ for i in range(len(self.LOOPS) - 1, -1, -1): if loop_type == self.LOOPS[i][0]: return self.LOOPS[i][1] raise InvalidLoopError(loop_type) - def loop_cont_label(self, loop_type): - ''' Returns the label for the given loop type which + """ Returns the label for the given loop type which continues the loop. loop_type must be one of 'FOR', 'WHILE', 'DO' - ''' + """ for i in range(len(self.LOOPS) - 1, -1, -1): if loop_type == self.LOOPS[i][0]: return self.LOOPS[i][2] raise InvalidLoopError(loop_type) - @classmethod def default_value(cls, type_, value): # TODO: This function must be moved to api.xx - ''' Returns a list of bytes (as hexadecimal 2 char string) - ''' + """ Returns a list of bytes (as hexadecimal 2 char string) + """ assert isinstance(type_, symbols.TYPE) assert type_.is_basic @@ -1100,12 +1054,11 @@ def default_value(cls, type_, value): # TODO: This function must be moved to ap return result[:type_.size] - @staticmethod def array_default_value(type_, values): - ''' Returns a list of bytes (as hexadecimal 2 char string) + """ Returns a list of bytes (as hexadecimal 2 char string) which represents the array initial value. - ''' + """ if not isinstance(values, list): return Translator.default_value(type_, values.value) @@ -1115,7 +1068,6 @@ def array_default_value(type_, values): return l - @staticmethod def has_control_chars(i): """ Returns true if the passed token is an unknown string @@ -1142,20 +1094,20 @@ def has_control_chars(i): return False - #endregion - ''' END ''' + # endregion + """ END """ class VarTranslator(TranslatorVisitor): - ''' Var Translator + """ Var Translator This translator emits memory var space - ''' + """ + def visit_LABEL(self, node): self.emit('label', node.mangled) for tmp in node.aliased_by: self.emit('label', tmp.mangled) - def visit_VARDECL(self, node): entry = node.entry if not entry.accessed: @@ -1174,12 +1126,12 @@ def visit_VARDECL(self, node): self.emit('var', entry.mangled, entry.size) else: if isinstance(entry.default_value, symbols.CONST) and \ - entry.default_value.token == 'CONST': - self.emit('varx', node.mangled, self.TSUFFIX(node.type_), [self.traverse_const(entry.default_value)]) + entry.default_value.token == 'CONST': + self.emit('varx', node.mangled, self.TSUFFIX(node.type_), + [self.traverse_const(entry.default_value)]) else: self.emit('vard', node.mangled, Translator.default_value(node.type_, entry.default_value)) - def visit_ARRAYDECL(self, node): entry = node.entry if not entry.accessed: @@ -1217,8 +1169,9 @@ def visit_ARRAYDECL(self, node): class UnaryOpTranslator(TranslatorVisitor): - ''' UNARY sub-visitor. E.g. -a or bNot pi - ''' + """ UNARY sub-visitor. E.g. -a or bNot pi + """ + def visit_MINUS(self, node): yield node.operand self.emit('neg' + self.TSUFFIX(node.type_), node.t, node.operand.t) @@ -1252,11 +1205,11 @@ def visit_ADDRESS(self, node): class BuiltinTranslator(TranslatorVisitor): - ''' BUILTIN functions visitor. Eg. LEN(a$) or SIN(x) - ''' + """ BUILTIN functions visitor. Eg. LEN(a$) or SIN(x) + """ REQUIRES = backend.REQUIRES - #region STRING Functions + # region STRING Functions def visit_INKEY(self, node): self.emit('call', 'INKEY', Type.string.size) backend.REQUIRES.add('inkey.asm') @@ -1267,7 +1220,7 @@ def visit_IN(self, node): def visit_CODE(self, node): self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), node.operand.t) if node.operand.token != 'STRING' and node.operand.token != 'VAR' and node.operand.t != '_': - self.emit('fparamu8', 1) # If the argument is not a variable, it must be freed + self.emit('fparamu8', 1) # If the argument is not a variable, it must be freed else: self.emit('fparamu8', 0) @@ -1304,12 +1257,12 @@ def visit_RND(self, node): # A special "ZEROARY" function with no parameters self.emit('call', 'RND', Type.float_.size) backend.REQUIRES.add('random.asm') - #endregion + # endregion def visit_PEEK(self, node): self.emit('load' + self.TSUFFIX(node.type_), node.t, '*' + str(node.children[0].t)) - #region MATH Functions + # region MATH Functions def visit_SIN(self, node): self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) self.emit('call', 'SIN', node.size) @@ -1360,7 +1313,8 @@ def visit_SQR(self, node): self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) self.emit('call', 'SQRT', node.size) self.REQUIRES.add('sqrt.asm') - #endregion + + # endregion def visit_LBOUND(self, node): entry = node.operands[0] @@ -1385,8 +1339,8 @@ def visit_USR_STR(self, node): backend.REQUIRES.add('usr_str.asm') def visit_USR(self, node): - ''' Machine code call from basic - ''' + """ Machine code call from basic + """ self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), node.children[0].t) self.emit('call', 'USR', node.type_.size) backend.REQUIRES.add('usr.asm') @@ -1404,14 +1358,12 @@ def __init__(self, function_list): assert isinstance(x, symbols.FUNCTION) self.functions = function_list - def start(self): while self.functions: f = self.functions.pop(0) __DEBUG__('Translating function ' + f.__repr__()) self.visit(f) - def visit_FUNCTION(self, node): self.emit('label', node.mangled) if node.convention == CONVENTION.fastcall: @@ -1423,7 +1375,7 @@ def visit_FUNCTION(self, node): if not local_var.accessed: # HINT: This should never happens as values() is already filtered api.errmsg.warning_not_used(local_var.lineno, local_var.name) # HINT: Cannot optimize local variables now, since the offsets are already calculated - #if self.O_LEVEL > 1: + # if self.O_LEVEL > 1: # return if local_var.class_ == CLASS.array: @@ -1478,7 +1430,7 @@ def visit_FUNCTION(self, node): self.emit('exchg') offset = -local_var.offset if scope == SCOPE.local else local_var.offset - elems = reduce(lambda x, y: x * y, [x.count for x in local_var.bounds]) + elems = functools.reduce(lambda x, y: x * y, [x.count for x in local_var.bounds]) self.emit('param' + self.TSUFFIX(gl.BOUND_TYPE), elems) self.emit('paddr', offset, local_var.t) self.emit('fparamu16', local_var.t) @@ -1493,9 +1445,8 @@ def visit_FUNCTION(self, node): else: self.emit('leave', node.params.size) - def visit_FUNCDECL(self, node): - ''' Nested scope functions - ''' + """ Nested scope functions + """ self.functions.append(node.entry) - #raise InvalidOperatorError('FUNDECL') + # raise InvalidOperatorError('FUNDECL') diff --git a/asm.py b/asm.py index b1841e9dd..8d32ab168 100644 --- a/asm.py +++ b/asm.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: ts=4:et:sw=4 diff --git a/asmlex.py b/asmlex.py index ce31e60d7..825993914 100755 --- a/asmlex.py +++ b/asmlex.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim:ts=4:et: @@ -12,16 +12,14 @@ # ---------------------------------------------------------------------- import ply.lex as lex -import sys, os - - -FILENAME = '' # Current filename +import sys +FILENAME = '' # Current filename _tokens = ('STRING', 'NEWLINE', 'LABEL', - 'ID', 'COMMA', 'PLUS', 'MINUS', 'LP', 'RP', 'MUL', 'DIV', 'POW', - 'UMINUS', 'APO', 'INTEGER', 'ADDR' - ) + 'ID', 'COMMA', 'PLUS', 'MINUS', 'LP', 'RP', 'MUL', 'DIV', 'POW', + 'UMINUS', 'APO', 'INTEGER', 'ADDR' + ) reserved_instructions = { 'adc': 'ADC', @@ -92,19 +90,18 @@ 'srl': 'SRL', 'sub': 'SUB', 'xor': 'XOR', - } - +} -pseudo = { # pseudo ops +pseudo = { # pseudo ops 'align': 'ALIGN', 'org': 'ORG', 'defb': 'DEFB', 'defm': 'DEFB', - 'db' : 'DEFB', + 'db': 'DEFB', 'defs': 'DEFS', 'defw': 'DEFW', - 'ds' : 'DEFS', - 'dw' : 'DEFW', + 'ds': 'DEFS', + 'dw': 'DEFW', 'equ': 'EQU', 'proc': 'PROC', 'endp': 'ENDP', @@ -113,18 +110,16 @@ 'incbin': 'INCBIN', 'namespace': 'NAMESPACE', 'default': 'DEFAULT' - } - - -regs8 = {'a': 'A', - 'b': 'B', 'c': 'C', - 'd': 'D', 'e': 'E', - 'h': 'H', 'l': 'L', - 'i': 'I', 'r': 'R', - 'ixh': 'IXH', 'ixl': 'IXL', - 'iyh': 'IYH', 'iyl': 'IYL' - } +} +regs8 = {'a': 'A', + 'b': 'B', 'c': 'C', + 'd': 'D', 'e': 'E', + 'h': 'H', 'l': 'L', + 'i': 'I', 'r': 'R', + 'ixh': 'IXH', 'ixl': 'IXL', + 'iyh': 'IYH', 'iyl': 'IYL' + } regs16 = { 'af': 'AF', @@ -136,38 +131,34 @@ 'sp': 'SP' } - flags = { - 'z' : 'Z', - 'nz' : 'NZ', - 'nc' : 'NC', - 'po' : 'PO', - 'pe' : 'PE', - 'p' : 'P', - 'm' : 'M', + 'z': 'Z', + 'nz': 'NZ', + 'nc': 'NC', + 'po': 'PO', + 'pe': 'PE', + 'p': 'P', + 'm': 'M', } - preprocessor = { - 'init' : '_INIT', - 'line' : '_LINE' + 'init': '_INIT', + 'line': '_LINE' } - - # List of token names. -_tokens = _tokens \ - + tuple(reserved_instructions.values()) \ - + tuple(pseudo.values()) \ - + tuple(regs8.values()) \ - + tuple(regs16.values()) \ - + tuple(flags.values()) \ - + tuple(preprocessor.values()) +_tokens = sorted(_tokens \ + + tuple(reserved_instructions.values()) \ + + tuple(pseudo.values()) \ + + tuple(regs8.values()) \ + + tuple(regs16.values()) \ + + tuple(flags.values()) \ + + tuple(preprocessor.values())) def get_uniques(l): - ''' Returns a list with no repeated elements. - ''' + """ Returns a list with no repeated elements. + """ result = [] for i in l: @@ -175,16 +166,15 @@ def get_uniques(l): result.append(i) return result - tokens = get_uniques(_tokens) - + class Lexer(object): - ''' Own class lexer to allow multiple instances. + """ Own class lexer to allow multiple instances. This lexer is just a wrapper of the current FILESTACK[-1] lexer - ''' + """ states = ( ('preproc', 'exclusive'), ) @@ -193,14 +183,13 @@ class Lexer(object): def __set_lineno(self, value): - ''' Setter for lexer.lineno - ''' + """ Setter for lexer.lineno + """ self.lex.lineno = value - def __get_lineno(self): - ''' Getter for lexer.lineno - ''' + """ Getter for lexer.lineno + """ if self.lex is None: return 0 @@ -208,62 +197,56 @@ def __get_lineno(self): lineno = property(__get_lineno, __set_lineno) - def t_INITIAL_preproc_skip(self, t): r'[ \t]+' - pass # Ignore whitespaces and tabs - + pass # Ignore whitespaces and tabs def t_CHAR(self, t): r"'.'" # A single char - + t.value = ord(t.value[1]) t.type = 'INTEGER' return t - def t_HEXA(self, t): r'([0-9][0-9a-fA-F]*[hH])|(\$[0-9a-fA-F]+)|(0x[0-9a-fA-F]+)' if t.value[:2] == '0x': - t.value = t.value[2:] # Remove initial 0x + t.value = t.value[2:] # Remove initial 0x elif t.value[0] == '$': - t.value = t.value[1:] # Remove initial '$' + t.value = t.value[1:] # Remove initial '$' else: t.value = t.value[:-1] # Remove last 'h' - + t.value = int(t.value, 16) # Convert to decimal t.type = 'INTEGER' return t - def t_BIN(self, t): - r'(%[01]+)|([01]+[bB])' # A Binary integer + r'(%[01]+)|([01]+[bB])' # A Binary integer # Note 00B is a 0 binary, but # 00Bh is a 12 in hex. So this pattern must come # after HEXA - + if t.value[0] == '%': - t.value = t.value[1:] # Remove initial % + t.value = t.value[1:] # Remove initial % else: t.value = t.value[:-1] # Remove last 'b' - t.value = int(t.value, 2) # Convert to decimal + t.value = int(t.value, 2) # Convert to decimal t.type = 'INTEGER' return t - def t_INITIAL_preproc_INTEGER(self, t): r'[0-9]+' # an integer decimal number t.value = int(t.value) - + return t - - + def t_INITIAL_ID(self, t): r'[.]?[_a-zA-Z]([.]?[_a-zA-Z0-9]+)*[:]?' # Any identifier @@ -274,119 +257,103 @@ def t_INITIAL_ID(self, t): return t t.value = tmp.upper() # Convert it to uppercase, since our internal tables uses uppercase - id = tmp.lower() + id_ = tmp.lower() - t.type = reserved_instructions.get(id) + t.type = reserved_instructions.get(id_) if t.type is not None: return t - t.type = pseudo.get(id) + t.type = pseudo.get(id_) if t.type is not None: return t - t.type = regs8.get(id) + t.type = regs8.get(id_) if t.type is not None: return t - t.type = flags.get(id) + t.type = flags.get(id_) if t.type is not None: return t - t.type = regs16.get(id, 'ID') + t.type = regs16.get(id_, 'ID') if t.type == 'ID': t.value = tmp # Restores original value return t - def t_preproc_ID(self, t): r'[_a-zA-Z][_a-zA-Z0-9]*' # preprocessor directives t.type = preprocessor.get(t.value.lower(), 'ID') return t - def t_COMMA(self, t): - r',' - - return t + r',' + return t def t_ADDR(self, t): r'\$' return t - def t_LP(self, t): r'\(' - - return t + return t def t_RP(self, t): r'\)' - - return t + return t def t_PLUS(self, t): r'\+' return t - def t_MINUS(self, t): r'\-' return t - def t_MUL(self, t): r'\*' return t - def t_DIV(self, t): r'\/' return t - def t_POW(self, t): r'\^' return t - 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 - - return t + r'"[^"]*"' # a doubled quoted string + t.value = t.value[1:-1] # Remove quotes + return t def t_INITIAL_preproc_error(self, t): - ''' error handling rule - ''' + """ error handling rule + """ self.error("illegal character '%s'" % t.value[0]) - def t_INITIAL_preproc_CONTINUE(self, t): r'\\\r?\n' t.lexer.lineno += 1 - - # Allows line breaking + # Allows line breaking def t_COMMENT(self, t): r';.*' - - # Skip to end of line (except end of line) + # Skip to end of line (except end of line) def t_INITIAL_preproc_NEWLINE(self, t): r'\r?\n' @@ -396,81 +363,72 @@ def t_INITIAL_preproc_NEWLINE(self, t): return t - def t_INITIAL_SHARP(self, t): r'\#' - + if self.find_column(t) == 1: t.lexer.begin('preproc') else: self.error("illegal character '%s'" % t.value[0]) - def __init__(self): - ''' Creates a new GLOBAL lexer instance - ''' + """ Creates a new GLOBAL lexer instance + """ self.lex = None - self.filestack = [] # Current filename, and line number being parsed + self.filestack = [] # Current filename, and line number being parsed self.input_data = '' self.tokens = tokens - self.next_token = None # if set to something, this will be returned once - + self.next_token = None # if set to something, this will be returned once def input(self, str): - ''' Defines input string, removing current lexer. - ''' + """ Defines input string, removing current lexer. + """ self.input_data = str - self.lex = lex.lex(object = self) + self.lex = lex.lex(object=self) self.lex.input(self.input_data) - def token(self): return self.lex.token() - def find_column(self, token): - ''' Compute column: + """ Compute column: - token is a token instance - ''' + """ i = token.lexpos while i > 0: if self.input_data[i - 1] == '\n': break i -= 1 - + column = token.lexpos - i + 1 - + return column + def msg(self, str_): + """ Prints an error msg. + """ + print('%s:%i %s' % (FILENAME, self.lex.lineno, str_)) + + def error(self, str_): + """ Prints an error msg, and exits. + """ + self.msg('Error: %s' % str_) - def msg(self, str): - ''' Prints an error msg. - ''' - print '%s:%i %s' % (FILENAME, self.lex.lineno, str) - - - def error(self, str): - ''' Prints an error msg, and exits. - ''' - self.msg('Error: %s' % str) - sys.exit(1) - - - def warning(self, str): - ''' Emmits a warning and continue execution. - ''' - self.msg('Warning: %s' % str) + + def warning(self, str_): + """ Emits a warning and continue execution. + """ + self.msg('Warning: %s' % str_) -# --------------------- PREPROCESOR FUNCTIONS ------------------- +# --------------------- PREPROCESSOR FUNCTIONS ------------------- # Needed for states -tmp = lex.lex(object = Lexer(), lextab = 'zxbasmlextab') +tmp = lex.lex(object=Lexer(), lextab='zxbasmlextab') if __name__ == '__main__': tmp.input(open(sys.argv[1]).read()) tok = tmp.token() while tok: - print tok + print(tok) tok = tmp.token() - diff --git a/asmparse.py b/asmparse.py index ec4da0318..6b767dc80 100755 --- a/asmparse.py +++ b/asmparse.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: et:ts=4:sw=4: @@ -28,20 +28,17 @@ FLAG_use_BASIC = False # Whether to use a BASIC loader or not FLAG_autorun = False # Set to true if you want the BASIC loader to autostart in the output .tzx/.tap file - -FILE_input = '' # Current file being processed +FILE_input = '' # Current file being processed FILE_output = '' # Output filename FILE_output_ext = 'bin' # Default output file extension FILE_stderr = '' # If not None, the error-msg output file name. - -ORG = 0 # Origin of CODE +ORG = 0 # Origin of CODE INITS = [] -MEMORY = None # Memory for instructions (Will be initialized with a Memory() instance) +MEMORY = None # Memory for instructions (Will be initialized with a Memory() instance) AUTORUN_ADDR = None # Where to start the execution automatically - -REGS16 = ('BC', 'DE', 'HL', 'SP', 'IX', 'IY') # 16 Bits registers +REGS16 = ('BC', 'DE', 'HL', 'SP', 'IX', 'IY') # 16 Bits registers REGS8 = ('A', 'B', 'C', 'D', 'E', 'H', 'L', 'IXh', 'IXl', 'IYh', 'IYl') precedence = (('left', 'PLUS', 'MINUS'), @@ -49,24 +46,25 @@ ('right', 'POW'), ('right', 'UMINUS'),) - MAX_MEM = 65535 # Max memory limit -DOT = '.' # NAMESPACE separator -NAMESPACE = '' # Current namespace (defaults to ''). It's a prefix added to each global label +DOT = '.' # NAMESPACE separator +NAMESPACE = '' # Current namespace (defaults to ''). It's a prefix added to each global label + class Asm(AsmInstruction): - ''' Class extension to AsmInstruction with a short name :-P + """ Class extension to AsmInstruction with a short name :-P and will trap some exceptions and convert them to error msgs. It will also record source line - ''' + """ + def __init__(self, lineno, asm, arg=None): self.lineno = lineno if asm not in ('DEFB', 'DEFS', 'DEFW'): try: AsmInstruction.__init__(self, asm, arg) - except Error, v: + except Error as v: error(lineno, v.msg) self.pending = len([x for x in self.arg if isinstance(x, Expr) and x.try_eval() is None]) > 0 @@ -84,16 +82,15 @@ def __init__(self, lineno, asm, arg=None): self.arg_num = len(self.arg) - def bytes(self): - ''' Returns opcodes - ''' + """ Returns opcodes + """ if self.asm not in ('DEFB', 'DEFS', 'DEFW'): if self.pending: - tmp = self.arg # Saves current arg temporarily + tmp = self.arg # Saves current arg temporarily self.arg = tuple([0] * self.arg_num) result = AsmInstruction.bytes(self) - self.arg = tmp # And recovers it + self.arg = tmp # And recovers it return result @@ -110,13 +107,13 @@ def bytes(self): N = self.arg[0] if isinstance(N, Expr): N = N.eval() - return tuple([0] * N) # ?? + return tuple([0] * N) # ?? args = self.argval() num = args[1] & 0xFF return tuple([num] * args[0]) - if self.pending: # DEFW + if self.pending: # DEFW return tuple([0] * 2 * self.arg_num) result = () @@ -127,16 +124,15 @@ def bytes(self): return result - def argval(self): - ''' Solve args values or raise errors if not + """ Solve args values or raise errors if not defined yet - ''' + """ if self.asm in ('DEFB', 'DEFS', 'DEFW'): 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 self.asm.split(' ')[0] in ('JR', 'DJNZ'): # A relative jump? + 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') @@ -144,32 +140,33 @@ def argval(self): class Container(object): - ''' Single class container - ''' + """ Single class container + """ + def __init__(self, item, lineno): - ''' Item to store - ''' + """ Item to store + """ self.item = item self.lineno = lineno class Expr(Ast): - ''' A class derived from AST that will + """ A class derived from AST that will recursively parse its nodes and return the value - ''' - ignore = True # Class flag + """ + ignore = True # Class flag funct = { '-': lambda x, y: x - y, '+': lambda x, y: x + y, '*': lambda x, y: x * y, '/': lambda x, y: x / y, '^': lambda x, y: x ** y - } + } - def __init__(self, symbol = None): - ''' Initializes ancestor attributes, and + def __init__(self, symbol=None): + """ Initializes ancestor attributes, and ignore flags. - ''' + """ Ast.__init__(self) self.symbol = symbol @@ -200,9 +197,9 @@ def right(self, value): self.children = [None, value] def eval(self): - ''' Recursively evals the node. Exits with an + """ Recursively evals the node. Exits with an error if not resolved. - ''' + """ Expr.ignore = False result = self.try_eval() Expr.ignore = True @@ -210,9 +207,9 @@ def eval(self): return result def try_eval(self): - ''' Recursively evals the node. Returns None + """ Recursively evals the node. Returns None if it is still unresolved. - ''' + """ item = self.symbol.item if isinstance(item, int): @@ -254,12 +251,12 @@ def try_eval(self): return None - class Label(object): - ''' A class to store Label information (NAME, linenumber and Address) - ''' + """ A class to store Label information (NAME, linenumber and Address) + """ + def __init__(self, name, lineno, value=None, local=False, namespace=None): - ''' Defines a Label object: + """ Defines a Label object: - name : The label name. e.g. __LOOP - lineno : Where was this label defined. - address : Memory address or numeric value this label refers @@ -267,7 +264,7 @@ def __init__(self, name, lineno, value=None, local=False, namespace=None): - local : whether this is a local label or a global one - namespace: If the label is DECLARED (not accessed), this is its prefixed namespace - ''' + """ self._name = name self.lineno = lineno self.value = value @@ -277,14 +274,13 @@ def __init__(self, name, lineno, value=None, local=False, namespace=None): @property def defined(self): - ''' Returns whether it has a value already or not. - ''' + """ Returns whether it has a value already or not. + """ return self.value is not None - def define(self, value, lineno, namespace=None): - ''' Defines label value. It can be anything. Even an AST - ''' + """ Defines label value. It can be anything. Even an AST + """ if self.defined: error(lineno, "label '%s' already defined at line %i" % (self.name, self.lineno)) @@ -292,10 +288,9 @@ def define(self, value, lineno, namespace=None): self.lineno = lineno self.namespace = NAMESPACE if namespace is None else namespace - def resolve(self, lineno): - ''' Evaluates label value. Exits with error (unresolved) if value is none - ''' + """ Evaluates label value. Exits with error (unresolved) if value is none + """ if not self.defined: error(lineno, "Undeclared label '%s'" % self.name) @@ -312,63 +307,58 @@ def name(self): return self.current_namespace + self._name - class Memory(object): - ''' A class to describe memory - ''' + """ A class to describe memory + """ + def __init__(self, org=0): - ''' Initializes the origin of code. - 0 by default ''' + """ Initializes the origin of code. + 0 by default """ self.index = org # ORG address (can be changed on the fly) self.memory_bytes = {} # An array (associative) containing memory bytes self.local_labels = [{}] # Local labels in the current memory scope self.global_labels = self.local_labels[0] # Global memory labels - self.orgs = {} # Origins of code for asm mnemonics. This will store corresponding asm instructions - self.ORG = org # last ORG value set + self.orgs = {} # Origins of code for asm mnemonics. This will store corresponding asm instructions + self.ORG = org # last ORG value set self.scopes = [] - def enter_proc(self, lineno): - ''' Enters (pushes) a new context - ''' + """ Enters (pushes) a new context + """ self.local_labels.append({}) # Add a new context self.scopes.append(lineno) __DEBUG__('Entering scope level %i at line %i' % (len(self.scopes), lineno)) - def set_org(self, value, lineno): - ''' Sets a new ORG value - ''' + """ Sets a new ORG value + """ if value < 0 or value > MAX_MEM: error(lineno, "Memory ORG out of range [0 .. 65535]. Current value: %i" % value) self.index = self.ORG = value - @property def org(self): - ''' Returns current ORG index - ''' + """ Returns current ORG index + """ return self.index - def __set_byte(self, byte, lineno): - ''' Sets a byte at the current location, + """ Sets a byte at the current location, and increments org in one. Raises an error if org > MAX_MEMORY - ''' + """ if byte < 0 or byte > 255: error(lineno, 'Invalid byte value %i' % byte) self.memory_bytes[self.org] = byte self.index += 1 # Increment current memory pointer - def exit_proc(self, lineno): - ''' Exits current procedure. Local labels are transferred to global + """ Exits current procedure. Local labels are transferred to global scope unless they have been marked as local ones. Raises an error if no current local context (stack underflow) - ''' + """ __DEBUG__('Exiting current scope from lineno %i' % lineno) if len(self.local_labels) <= 1: @@ -392,29 +382,26 @@ def exit_proc(self, lineno): self.local_labels.pop() # Removes current context self.scopes.pop() - def set_memory_slot(self): if self.org not in self.orgs.keys(): 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): - ''' This will insert an asm instruction at the current memory position + """ This will insert an asm instruction at the current memory position in a t-uple as (nmenomic, params). It will also insert the opcodes at the memory_bytes - ''' + """ self.set_memory_slot() - self.orgs[self.org] += (asm, ) + self.orgs[self.org] += (asm,) for byte in asm.bytes(): self.__set_byte(byte, asm.lineno) - def dump(self): - ''' Returns a t-uple containing code ORG, and a list of OUTPUT - ''' + """ Returns a t-uple containing code ORG, and a list of OUTPUT + """ org = min(self.memory_bytes.keys()) # Org is the lowest one OUTPUT = [] align = [] @@ -448,14 +435,13 @@ def dump(self): return (org, OUTPUT) - def declare_label(self, label, lineno, value=None, local=False, namespace=None): - ''' Sets a label with the given value or with the current address (org) + """ Sets a label with the given value or with the current address (org) if no value is passed. Exits with error if label already set, otherwise return the label object - ''' + """ if label[0] != DOT: if namespace is None: namespace = NAMESPACE @@ -474,15 +460,14 @@ def declare_label(self, label, lineno, value=None, local=False, namespace=None): self.local_labels[-1][ex_label] = Label(label, lineno, value, local, namespace) self.set_memory_slot() - self.memory_bytes[self.org] += ('%s:' % ex_label, ) + self.memory_bytes[self.org] += ('%s:' % ex_label,) return self.local_labels[-1][ex_label] - def get_label(self, label, lineno): - ''' Returns a label in the current context or in the global one. + """ Returns a label in the current context or in the global one. If the label does not exists, creates a new one and returns it. - ''' + """ global NAMESPACE if label[0] != DOT: @@ -502,14 +487,13 @@ def get_label(self, label, lineno): return result - def set_label(self, label, lineno, local=False): - ''' Sets a label, lineno and local flag in the current scope + """ Sets a label, lineno and local flag in the current scope (even if it exist in previous scopes). If the label exist in the current scope, changes it flags. The resulting label is returned. - ''' + """ if label[0] != DOT: ex_label = NAMESPACE + label # expanded name else: @@ -529,47 +513,47 @@ def set_label(self, label, lineno, local=False): return result - - # -------- GRAMMAR RULES for the preprocessor --------- def p_start(p): - ''' start : program + """ start : program | program endline - ''' + """ + def p_program_endline(p): - ''' endline : END NEWLINE - ''' + """ endline : END NEWLINE + """ + def p_program_endline2(p): - ''' endline : END expr NEWLINE + """ endline : END expr NEWLINE | END pexpr NEWLINE - ''' + """ global AUTORUN_ADDR AUTORUN_ADDR = p[2].eval() def p_program(p): - ''' program : line - ''' + """ 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]) def p_program_line(p): - ''' program : program line - ''' + """ 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]) def p_def_label(p): - ''' line : ID EQU expr NEWLINE + """ line : ID EQU expr NEWLINE | ID EQU pexpr NEWLINE - ''' + """ p[0] = None __DEBUG__("Declaring '%s%s' in %i" % (NAMESPACE, p[1], p.lineno(1))) @@ -577,8 +561,8 @@ def p_def_label(p): def p_line_label(p): - ''' line : LABEL NEWLINE - ''' + """ line : LABEL NEWLINE + """ p[0] = None # Nothing to append __DEBUG__("Declaring '%s%s' (value %04Xh) in %i" % (NAMESPACE, p[1], MEMORY.org, p.lineno(1))) @@ -586,8 +570,8 @@ def p_line_label(p): def p_line_label_asm(p): - ''' line : LABEL asm NEWLINE - ''' + """ line : LABEL asm NEWLINE + """ p[0] = p[2] __DEBUG__("Declaring '%s' (value %04Xh) in %i" % (p[1], MEMORY.org, p.lineno(1))) @@ -595,19 +579,19 @@ def p_line_label_asm(p): def p_line_asm(p): - ''' line : asm NEWLINE - ''' + """ line : asm NEWLINE + """ p[0] = p[1] def p_line_newline(p): - ''' line : NEWLINE - ''' + """ line : NEWLINE + """ p[0] = None def p_asm_ld8(p): - ''' asm : LD reg8 COMMA reg8_hl + """ asm : LD reg8 COMMA reg8_hl | LD reg8_hl COMMA reg8 | LD reg8 COMMA reg8 | LD SP COMMA HL @@ -626,7 +610,7 @@ def p_asm_ld8(p): | LD reg8 COMMA reg8i | LD reg8i COMMA regBCDE | LD reg8i COMMA reg8i - ''' + """ if p[2] in ('H', 'L') and p[4] in ('IXH', 'IXL', 'IYH', 'IYL'): p[0] = None error(p.lineno(0), "Unexpected token '%s'" % p[4]) @@ -634,32 +618,32 @@ def p_asm_ld8(p): p[0] = Asm(p.lineno(1), 'LD %s,%s' % (p[2], p[4])) -def p_LDa(p): # Remaining LD A,... and LD...,A instructions - ''' asm : LD A COMMA LP BC RP +def p_LDa(p): # Remaining LD A,... and LD...,A instructions + """ asm : LD A COMMA LP BC RP | LD A COMMA LP DE RP | LD LP BC RP COMMA A | LD LP DE RP COMMA A - ''' + """ p[0] = Asm(p.lineno(1), 'LD ' + ''.join(p[2:])) def p_PROC(p): - ''' asm : PROC - ''' - p[0] = None # Start of a PROC scope + """ asm : PROC + """ + p[0] = None # Start of a PROC scope MEMORY.enter_proc(p.lineno(1)) def p_ENDP(p): - ''' asm : ENDP - ''' - p[0] = None # End of a PROC scope + """ asm : ENDP + """ + p[0] = None # End of a PROC scope MEMORY.exit_proc(p.lineno(1)) def p_LOCAL(p): - ''' asm : LOCAL id_list - ''' + """ asm : LOCAL id_list + """ p[0] = None for label, line in p[2]: __DEBUG__("Setting label '%s' as local at line %i" % (label, line)) @@ -668,79 +652,79 @@ def p_LOCAL(p): def p_idlist(p): - ''' id_list : ID - ''' - p[0] = ((p[1], p.lineno(1)), ) + """ id_list : ID + """ + p[0] = ((p[1], p.lineno(1)),) def p_idlist_id(p): - ''' id_list : id_list COMMA ID - ''' - p[0] = p[1] + ((p[3], p.lineno(3)), ) + """ id_list : id_list COMMA ID + """ + p[0] = p[1] + ((p[3], p.lineno(3)),) -def p_DEFB(p): # Define bytes - ''' asm : DEFB number_list +def p_DEFB(p): # Define bytes + """ asm : DEFB number_list | DEFB STRING - ''' + """ p[0] = Asm(p.lineno(1), 'DEFB', p[2]) -def p_DEFS(p): # Define bytes - ''' asm : DEFS number_list - ''' +def p_DEFS(p): # Define bytes + """ asm : DEFS number_list + """ if len(p[2]) > 2: error(p.lineno(1), "too many arguments for DEFS") if len(p[2]) < 2: - num = Expr.makenode(Container(0, p.lineno(1))) # Defaults to 0 - p[2] = p[2] + (num, ) + num = Expr.makenode(Container(0, p.lineno(1))) # Defaults to 0 + p[2] = p[2] + (num,) p[0] = Asm(p.lineno(1), 'DEFS', p[2]) -def p_DEFW(p): # Define words - ''' asm : DEFW number_list - ''' +def p_DEFW(p): # Define words + """ asm : DEFW number_list + """ p[0] = Asm(p.lineno(1), 'DEFW', p[2]) def p_number_list(p): - ''' number_list : expr + """ number_list : expr | pexpr - ''' - p[0] = (p[1], ) + """ + p[0] = (p[1],) def p_number_list_number(p): - ''' number_list : number_list COMMA expr + """ number_list : number_list COMMA expr | number_list COMMA pexpr - ''' - p[0] = p[1] + (p[3], ) + """ + p[0] = p[1] + (p[3],) def p_asm_ldind_r8(p): - ''' asm : LD reg8_I COMMA reg8 + """ asm : LD reg8_I COMMA reg8 | LD reg8_I COMMA A - ''' + """ p[0] = Asm(p.lineno(1), 'LD %s,%s' % (p[2][0], p[4]), p[2][1]) def p_asm_ldr8_ind(p): - ''' asm : LD reg8 COMMA reg8_I + """ asm : LD reg8 COMMA reg8_I | LD A COMMA reg8_I - ''' + """ p[0] = Asm(p.lineno(1), 'LD %s,%s' % (p[2], p[4][0]), p[4][1]) def p_reg8_hl(p): - ''' reg8_hl : LP HL RP - ''' + """ reg8_hl : LP HL RP + """ p[0] = '(HL)' def p_ind8_I(p): - ''' reg8_I : LP IX PLUS expr RP + """ reg8_I : LP IX PLUS expr RP | LP IX MINUS expr RP | LP IY PLUS expr RP | LP IY MINUS expr RP @@ -748,7 +732,7 @@ def p_ind8_I(p): | LP IX MINUS pexpr RP | LP IY PLUS pexpr RP | LP IY MINUS pexpr RP - ''' + """ expr = p[4] if p[3] == '-': expr = Expr.makenode(Container('-', p.lineno(3)), expr) @@ -757,28 +741,28 @@ def p_ind8_I(p): def p_ex_af_af(p): - ''' asm : EX AF COMMA AF APO - ''' + """ asm : EX AF COMMA AF APO + """ p[0] = Asm(p.lineno(1), "EX AF,AF'") def p_ex_de_hl(p): - ''' asm : EX DE COMMA HL - ''' + """ asm : EX DE COMMA HL + """ p[0] = Asm(p.lineno(1), "EX DE,HL") def p_org(p): - ''' asm : ORG expr + """ asm : ORG expr | ORG pexpr - ''' + """ MEMORY.set_org(p[2].eval(), p.lineno(1)) def p_namespace(p): - ''' asm : NAMESPACE DEFAULT + """ asm : NAMESPACE DEFAULT | NAMESPACE ID - ''' + """ global NAMESPACE if p[2] != 'DEFAULT': @@ -790,9 +774,9 @@ def p_namespace(p): def p_align(p): - ''' asm : ALIGN expr + """ asm : ALIGN expr | ALIGN pexpr - ''' + """ align = p[2].eval() if align < 2: error(p.lineno(1), "ALIGN value must be greater than 1") @@ -801,8 +785,8 @@ def p_align(p): def p_incbin(p): - ''' asm : INCBIN STRING - ''' + """ asm : INCBIN STRING + """ try: filecontent = open(p[2], 'rb').read() except IOError: @@ -812,35 +796,35 @@ def p_incbin(p): def p_ex_sp_reg8(p): - ''' asm : EX LP SP RP COMMA reg16i + """ asm : EX LP SP RP COMMA reg16i | EX LP SP RP COMMA HL - ''' + """ p[0] = Asm(p.lineno(1), 'EX (SP),' + p[6]) def p_incdec(p): - ''' asm : INC inc_reg + """ asm : INC inc_reg | DEC inc_reg - ''' + """ p[0] = Asm(p.lineno(1), '%s %s' % (p[1], p[2])) def p_incdeci(p): - ''' asm : INC reg8_I + """ asm : INC reg8_I | DEC reg8_I - ''' + """ p[0] = Asm(p.lineno(1), '%s %s' % (p[1], p[2][0]), p[2][1]) def p_LD_reg_val(p): - ''' asm : LD reg8 COMMA expr + """ asm : LD reg8 COMMA expr | LD reg8 COMMA pexpr | LD reg16 COMMA expr | LD reg8_hl COMMA expr | LD A COMMA expr | LD SP COMMA expr | LD reg8i COMMA expr - ''' + """ s = 'LD %s,N' % p[2] if p[2] in REGS16: s += 'N' @@ -849,16 +833,15 @@ def p_LD_reg_val(p): def p_LD_regI_val(p): - ''' asm : LD reg8_I COMMA expr - ''' + """ asm : LD reg8_I COMMA expr + """ p[0] = Asm(p.lineno(1), 'LD %s,N' % p[2][0], (p[2][1], p[4])) - def p_JP_hl(p): - ''' asm : JP reg8_hl + """ asm : JP reg8_hl | JP LP reg16i RP - ''' + """ s = 'JP ' if p[2] == '(HL)': s += p[2] @@ -869,7 +852,7 @@ def p_JP_hl(p): def p_SBCADD(p): - ''' asm : SBC A COMMA reg8 + """ asm : SBC A COMMA reg8 | SBC A COMMA reg8i | SBC A COMMA A | SBC A COMMA reg8_hl @@ -898,108 +881,108 @@ def p_SBCADD(p): | ADD reg16i COMMA HL | ADD reg16i COMMA SP | ADD reg16i COMMA reg16i - ''' + """ p[0] = Asm(p.lineno(1), '%s %s,%s' % (p[1], p[2], p[4])) def p_arith_A_expr(p): - ''' asm : SBC A COMMA expr + """ asm : SBC A COMMA expr | SBC A COMMA pexpr | ADD A COMMA expr | ADD A COMMA pexpr | ADC A COMMA expr | ADC A COMMA pexpr - ''' + """ p[0] = Asm(p.lineno(1), '%s A,N' % p[1], p[4]) def p_arith_A_regI(p): - ''' asm : SBC A COMMA reg8_I + """ asm : SBC A COMMA reg8_I | ADD A COMMA reg8_I | ADC A COMMA reg8_I - ''' + """ p[0] = Asm(p.lineno(1), '%s A,%s' % (p[1], p[4][0]), p[4][1]) def p_bitwiseop_reg(p): - ''' asm : bitwiseop reg8 + """ asm : bitwiseop reg8 | bitwiseop reg8i | bitwiseop A | bitwiseop reg8_hl - ''' + """ p[0] = Asm(p[1][1], '%s %s' % (p[1][0], p[2])) def p_bitwiseop_regI(p): - ''' asm : bitwiseop reg8_I - ''' + """ asm : bitwiseop reg8_I + """ p[0] = Asm(p[1][1], '%s %s' % (p[1][0], p[2][0]), p[2][1]) def p_bitwise_expr(p): - ''' asm : bitwiseop expr + """ asm : bitwiseop expr | bitwiseop pexpr - ''' + """ p[0] = Asm(p[1][1], '%s N' % p[1][0], p[2]) def p_bitwise(p): - ''' bitwiseop : OR + """ bitwiseop : OR | AND | XOR | SUB | CP - ''' + """ p[0] = (p[1], p.lineno(1)) def p_PUSH_POP(p): - ''' asm : PUSH AF + """ asm : PUSH AF | PUSH reg16 | POP AF | POP reg16 - ''' + """ p[0] = Asm(p.lineno(1), '%s %s' % (p[1], p[2])) -def p_LD_addr_reg(p): # Load address,reg - ''' asm : LD pexpr COMMA A +def p_LD_addr_reg(p): # Load address,reg + """ asm : LD pexpr COMMA A | LD pexpr COMMA reg16 | LD pexpr COMMA SP - ''' + """ p[0] = Asm(p.lineno(1), 'LD (NN),%s' % p[4], p[2]) -def p_LD_reg_addr(p): # Load address,reg - ''' asm : LD A COMMA pexpr +def p_LD_reg_addr(p): # Load address,reg + """ asm : LD A COMMA pexpr | LD reg16 COMMA pexpr | LD SP COMMA pexpr - ''' + """ p[0] = Asm(p.lineno(1), 'LD %s,(NN)' % p[2], p[4]) def p_ROTATE(p): - ''' asm : rotation reg8 + """ asm : rotation reg8 | rotation reg8_hl | rotation A - ''' + """ p[0] = Asm(p[1][1], '%s %s' % (p[1][0], p[2])) def p_ROTATE_ix(p): - ''' asm : rotation reg8_I - ''' + """ asm : rotation reg8_I + """ p[0] = Asm(p[1][1], '%s %s' % (p[1][0], p[2][0]), p[2][1]) def p_BIT(p): - ''' asm : bitop expr COMMA A + """ asm : bitop expr COMMA A | bitop pexpr COMMA A | bitop expr COMMA reg8 | bitop pexpr COMMA reg8 | bitop expr COMMA reg8_hl | bitop pexpr COMMA reg8_hl - ''' + """ bit = p[2].eval() if bit < 0 or bit > 7: error(p.lineno(3), 'Invalid bit position %i. Must be in [0..7]' % bit) @@ -1008,9 +991,9 @@ def p_BIT(p): def p_BIT_ix(p): - ''' asm : bitop expr COMMA reg8_I + """ asm : bitop expr COMMA reg8_I | bitop pexpr COMMA reg8_I - ''' + """ bit = p[2].eval() if bit < 0 or bit > 7: error(p.lineno(3), 'Invalid bit position %i. Must be in [0..7]' % bit) @@ -1019,15 +1002,15 @@ def p_BIT_ix(p): def p_bitop(p): - ''' bitop : BIT + """ bitop : BIT | RES | SET - ''' + """ p[0] = p[1] def p_rotation(p): - ''' rotation : RR + """ rotation : RR | RL | RRC | RLC @@ -1035,107 +1018,107 @@ def p_rotation(p): | SLL | SRA | SRL - ''' + """ p[0] = (p[1], p.lineno(1)) -def p_reg_inc(p): # INC/DEC registers and (HL) - ''' inc_reg : SP +def p_reg_inc(p): # INC/DEC registers and (HL) + """ inc_reg : SP | reg8 | reg16 | reg8_hl | A | reg8i - ''' + """ p[0] = p[1] def p_reg8(p): - ''' reg8 : H + """ reg8 : H | L | regBCDE - ''' + """ p[0] = p[1] def p_regBCDE(p): - ''' regBCDE : B + """ regBCDE : B | C | D | E - ''' + """ p[0] = p[1] def p_reg8i(p): - ''' reg8i : IXH + """ reg8i : IXH | IXL | IYH | IYL - ''' + """ p[0] = p[1] def p_reg16(p): - ''' reg16 : BC + """ reg16 : BC | DE | HL | reg16i - ''' + """ p[0] = p[1] def p_reg16i(p): - ''' reg16i : IX + """ reg16i : IX | IY - ''' + """ p[0] = p[1] def p_jp(p): - ''' asm : JP jp_flags COMMA expr + """ asm : JP jp_flags COMMA expr | JP jp_flags COMMA pexpr | CALL jp_flags COMMA expr | CALL jp_flags COMMA pexpr - ''' + """ p[0] = Asm(p.lineno(1), '%s %s,NN' % (p[1], p[2]), p[4]) def p_ret(p): - ''' asm : RET jp_flags - ''' + """ asm : RET jp_flags + """ p[0] = Asm(p.lineno(1), 'RET %s' % p[2]) def p_jpflags_other(p): - ''' jp_flags : P + """ jp_flags : P | M | PO | PE | jr_flags - ''' + """ p[0] = p[1] def p_jr(p): - ''' asm : JR jr_flags COMMA expr + """ asm : JR jr_flags COMMA expr | JR jr_flags COMMA pexpr - ''' + """ p[4] = Expr.makenode(Container('-', p.lineno(3)), p[4], Expr.makenode(Container(MEMORY.org + 2, p.lineno(1)))) p[0] = Asm(p.lineno(1), 'JR %s,N' % p[2], p[4]) def p_jr_flags(p): - ''' jr_flags : Z + """ jr_flags : Z | C | NZ | NC - ''' + """ p[0] = p[1] def p_jrjp(p): - ''' asm : JP expr + """ asm : JP expr | JR expr | CALL expr | DJNZ expr @@ -1143,7 +1126,7 @@ def p_jrjp(p): | JR pexpr | CALL pexpr | DJNZ pexpr - ''' + """ if p[1] in ('JR', 'DJNZ'): op = 'N' p[2] = Expr.makenode(Container('-', p.lineno(1)), p[2], Expr.makenode(Container(MEMORY.org + 2, p.lineno(1)))) @@ -1154,8 +1137,8 @@ def p_jrjp(p): def p_rst(p): - ''' asm : RST expr - ''' + """ asm : RST expr + """ val = p[2].eval() if val not in (0, 8, 16, 24, 32, 40, 48, 56): @@ -1165,8 +1148,8 @@ def p_rst(p): def p_im(p): - ''' asm : IM expr - ''' + """ asm : IM expr + """ val = p[2].eval() if val not in (0, 1, 2): error(p.lineno(1), 'Invalid IM number %i' % val) @@ -1175,33 +1158,33 @@ def p_im(p): def p_in(p): - ''' asm : IN A COMMA LP C RP + """ asm : IN A COMMA LP C RP | IN reg8 COMMA LP C RP - ''' + """ p[0] = Asm(p.lineno(1), 'IN %s,(C)' % p[2]) def p_out(p): - ''' asm : OUT LP C RP COMMA A + """ asm : OUT LP C RP COMMA A | OUT LP C RP COMMA reg8 - ''' + """ p[0] = Asm(p.lineno(1), 'OUT (C),%s' % p[6]) def p_in_expr(p): - ''' asm : IN A COMMA pexpr - ''' + """ asm : IN A COMMA pexpr + """ p[0] = Asm(p.lineno(1), 'IN A,(N)', p[4]) def p_out_expr(p): - ''' asm : OUT pexpr COMMA A - ''' + """ asm : OUT pexpr COMMA A + """ p[0] = Asm(p.lineno(1), 'OUT (N),A', p[2]) def p_single(p): - ''' asm : NOP + """ asm : NOP | EXX | CCF | SCF @@ -1236,12 +1219,12 @@ def p_single(p): | RRCA | RLD | RRD - ''' - p[0] = Asm(p.lineno(1), p[1]) # Single instruction + """ + p[0] = Asm(p.lineno(1), p[1]) # Single instruction def p_expr_div_expr(p): - ''' expr : expr PLUS expr + """ expr : expr PLUS expr | expr MINUS expr | expr MUL expr | expr DIV expr @@ -1256,56 +1239,56 @@ def p_expr_div_expr(p): | expr MUL pexpr | expr DIV pexpr | expr POW pexpr - ''' + """ p[0] = Expr.makenode(Container(p[2], p.lineno(2)), p[1], p[3]) def p_expr_lprp(p): - ''' pexpr : LP expr RP - ''' + """ pexpr : LP expr RP + """ p[0] = p[2] def p_expr_uminus(p): - ''' expr : MINUS expr %prec UMINUS - ''' + """ expr : MINUS expr %prec UMINUS + """ p[0] = Expr.makenode(Container('-', p.lineno(1)), p[2]) def p_expr_int(p): - ''' expr : INTEGER - ''' + """ expr : INTEGER + """ p[0] = Expr.makenode(Container(int(p[1]), p.lineno(1))) def p_expr_label(p): - ''' expr : ID - ''' + """ expr : ID + """ p[0] = Expr.makenode(Container(MEMORY.get_label(p[1], p.lineno(1)), p.lineno(1))) def p_expr_addr(p): - ''' expr : ADDR - ''' # The current instruction address + """ expr : ADDR + """ # The current instruction address p[0] = Expr.makenode(Container(MEMORY.org, p.lineno(1))) # Some preprocessor directives def p_preprocessor_line(p): - ''' asm : preproc_line - ''' + """ asm : preproc_line + """ p[0] = None def p_preprocessor_line_line(p): - ''' preproc_line : _LINE INTEGER - ''' + """ preproc_line : _LINE INTEGER + """ p.lexer.lineno = int(p[2]) + p.lexer.lineno - p.lineno(2) def p_preprocessor_line_line_file(p): - ''' preproc_line : _LINE INTEGER STRING - ''' + """ preproc_line : _LINE INTEGER STRING + """ global FILE_input p.lexer.lineno = int(p[2]) + p.lexer.lineno - p.lineno(3) - 1 @@ -1313,8 +1296,8 @@ def p_preprocessor_line_line_file(p): def p_preproc_line_init(p): - ''' preproc_line : _INIT ID - ''' + """ preproc_line : _INIT ID + """ INITS.append((p[2], p.lineno(2))) @@ -1332,24 +1315,24 @@ def p_error(p): def assemble(input): - ''' Assembles input string, and leave the result in the + """ Assembles input string, and leave the result in the MEMORY global object - ''' + """ global MEMORY if MEMORY is None: MEMORY = Memory() - parser.parse(input, lexer = LEXER, debug = OPTIONS.Debug.value > 2) + parser.parse(input, lexer=LEXER, debug=OPTIONS.Debug.value > 2) if len(MEMORY.scopes): error(MEMORY.scopes[-1], 'Missing ENDP to close this scope') def generate_binary(outputfname, format): - ''' Ouputs the memory binary to the + """ Ouputs the memory binary to the output filename using one of the given formats: tap, tzx or bin - ''' + """ global AUTORUN_ADDR org, binary = MEMORY.dump() @@ -1394,14 +1377,13 @@ def generate_binary(outputfname, format): t.dump(outputfname) else: - f = open(outputfname, 'wb') - f.write(''.join([chr(x) for x in binary])) - + with open(outputfname, 'wb') as f: + f.write(bytearray(binary)) def main(argv): - ''' This is a test and will assemble the file in argv[0] - ''' + """ This is a test and will assemble the file in argv[0] + """ global FILE_input, MEMORY, INITS, AUTORUN_ADDR MEMORY = Memory() @@ -1412,25 +1394,24 @@ def main(argv): OPTIONS.stderr.value = open('wt', FILE_stderr) asmlex.FILENAME = FILE_input = argv[0] - input = open(FILE_input, 'rt').read() - assemble(input) + input_ = open(FILE_input, 'rt').read() + assemble(input_) generate_binary(FILE_output, FILE_output_ext) + parser = yacc.yacc(method='LALR', tabmodule='zxbasmtab', debug=OPTIONS.Debug.value > 2) # ------- ERROR And Warning messages ---------------- -def msg(lineno, str): - OPTIONS.stderr.value.write('%s:%i: %s\n' % (FILE_input, lineno, str)) +def msg(lineno, str_): + OPTIONS.stderr.value.write('%s:%i: %s\n' % (FILE_input, lineno, str_)) -def error(lineno, str): - msg(lineno, 'Error: %s' % str) +def error(lineno, str_): + msg(lineno, 'Error: %s' % str_) sys.exit(1) -def warning(lineno, str): - msg(lineno, 'Warning: %s' % str) - - +def warning(lineno, str_): + msg(lineno, 'Warning: %s' % str_) diff --git a/ast_/__init__.py b/ast_/__init__.py index 24f2c0c42..074fdfa04 100644 --- a/ast_/__init__.py +++ b/ast_/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from ast import * -from tree import Tree +from .ast import * +from .tree import Tree diff --git a/ast_/ast.py b/ast_/ast.py index fb0d221ee..033d5ee92 100644 --- a/ast_/ast.py +++ b/ast_/ast.py @@ -10,7 +10,7 @@ # ---------------------------------------------------------------------- import types -from tree import Tree +from .tree import Tree # ---------------------------------------------------------------------- diff --git a/backend/__16bit.py b/backend/__16bit.py index bb4c4cdc7..2defa40f8 100644 --- a/backend/__16bit.py +++ b/backend/__16bit.py @@ -9,8 +9,8 @@ # comparation intermediate-code traductions # -------------------------------------------------------------- -from __common import REQUIRES, is_int, log2, is_2n, _int_ops, tmp_label -from __8bit import _8bit_oper +from .__common import REQUIRES, is_int, log2, is_2n, _int_ops, tmp_label +from .__8bit import _8bit_oper # ----------------------------------------------------- diff --git a/backend/__32bit.py b/backend/__32bit.py index f348487dc..cd3cbeaf3 100644 --- a/backend/__32bit.py +++ b/backend/__32bit.py @@ -10,12 +10,15 @@ # comparation intermediate-code traductions # -------------------------------------------------------------- -from __common import REQUIRES, is_int, log2, is_2n, _int_ops, tmp_label -from __8bit import _8bit_oper +from .__common import REQUIRES, is_int, _int_ops, tmp_label +from .__8bit import _8bit_oper + # ----------------------------------------------------- # 32 bits operands # ----------------------------------------------------- + + def int32(op): ''' Returns a 32 bit operand converted to 32 bits unsigned int. Negative numbers are returned in 2 complement. @@ -26,7 +29,7 @@ def int32(op): return (result >> 16, result & 0xFFFF) -def _32bit_oper(op1, op2 = None, reversed = False, preserveHL = False): +def _32bit_oper(op1, op2=None, reversed=False, preserveHL=False): ''' Returns pop sequence for 32 bits operands 1st operand in HLDE, 2nd operand remains in the stack @@ -49,9 +52,9 @@ def _32bit_oper(op1, op2 = None, reversed = False, preserveHL = False): op = op2 if op2 is not None else op1 - int1 = False # whether op1 (2nd operand) is integer - int2 = False # whether op1 (2nd operand) is integer - + int1 = False # whether op1 (2nd operand) is integer + int2 = False # whether op1 (2nd operand) is integer + indirect = (op[0] == '*') if indirect: op = op[1:] @@ -104,7 +107,6 @@ def _32bit_oper(op1, op2 = None, reversed = False, preserveHL = False): else: output.append('pop de') - if op2 is not None: op = op1 @@ -115,18 +117,18 @@ def _32bit_oper(op1, op2 = None, reversed = False, preserveHL = False): immediate = (op[0] == '#') if immediate: op = op[1:] - + if is_int(op): int2 = True op = int(op) - + if indirect: output.append('exx') if immediate: output.append('ld hl, %i' % (op & 0xFFFF)) else: output.append('ld hl, (%i)' % (op & 0xFFFF)) - + output.append('call __ILOAD32') output.append('push de') output.append('push hl') @@ -140,22 +142,22 @@ def _32bit_oper(op1, op2 = None, reversed = False, preserveHL = False): output.append('push bc') else: if indirect: - output.append('exx') # uses alternate set to put it on the stack + output.append('exx') # uses alternate set to put it on the stack if op[0] == '_': if immediate: output.append('ld hl, %s' % op) else: output.append('ld hl, (%s)' % op) else: - output.append('pop hl') # Pointers are only 16 bits *** + output.append('pop hl') # Pointers are only 16 bits *** output.append('call __ILOAD32') output.append('push de') output.append('push hl') output.append('exx') REQUIRES.add('iload32.asm') - elif op[0] == '_': # an address - if int1 or op1[0] == '_': # If previous op was integer, we can use hl in advance + elif op[0] == '_': # an address + if int1 or op1[0] == '_': # If previous op was integer, we can use hl in advance tmp = output output = [] output.append('ld hl, (%s + 2)' % op) @@ -169,8 +171,7 @@ def _32bit_oper(op1, op2 = None, reversed = False, preserveHL = False): output.append('ld bc, (%s)' % op) output.append('push bc') else: - pass # 2nd operand remains in the stack - + pass # 2nd operand remains in the stack if op2 is not None and reversed: output.append('call __SWAP32') @@ -179,7 +180,6 @@ def _32bit_oper(op1, op2 = None, reversed = False, preserveHL = False): return output - # ----------------------------------------------------- # Arithmetic operations # ----------------------------------------------------- @@ -197,14 +197,14 @@ def _add32(ins): if _int_ops(op1, op2) is not None: o1, o2 = _int_ops(op1, op2) - if int(o2) == 0: # A + 0 = 0 + A = A => Do Nothing + if int(o2) == 0: # A + 0 = 0 + A = A => Do Nothing output = _32bit_oper(o1) output.append('push de') output.append('push hl') return output if op1[0] == '_' and op2[0] != '_': - op1, op2 = op2, op1 # swap them + op1, op2 = op2, op1 # swap them if op2[0] == '_': output = _32bit_oper(op1) @@ -223,13 +223,12 @@ def _add32(ins): output.append('ex de, hl') output.append('pop bc') output.append('adc hl, bc') - output.append('push hl') # High and low parts are reversed + output.append('push hl') # High and low parts are reversed output.append('push de') return output - def _sub32(ins): ''' Pops last 2 dwords from the stack and subtract them. Then push the result onto the stack. @@ -240,7 +239,7 @@ def _sub32(ins): op1, op2 = tuple(ins.quad[2:]) if is_int(op2): - if int(op2) == 0: # A - 0 = A => Do Nothing + if int(op2) == 0: # A - 0 = A => Do Nothing output = _32bit_oper(op1) output.append('push de') output.append('push hl') @@ -256,7 +255,6 @@ def _sub32(ins): return output - def _mul32(ins): ''' Multiplies two last 32bit values on top of the stack and and returns the value on top of the stack @@ -274,7 +272,7 @@ def _mul32(ins): if op2 == 1: output.append('push de') output.append('push hl') - return output # A * 1 = Nothing + return output # A * 1 = Nothing if op2 == 0: output.append('ld hl, 0') @@ -283,14 +281,13 @@ def _mul32(ins): return output output = _32bit_oper(op1, op2) - output.append('call __MUL32') # Inmmediate + output.append('call __MUL32') # Inmmediate output.append('push de') output.append('push hl') REQUIRES.add('mul32.asm') return output - def _divu32(ins): ''' Divides 2 32bit unsigned integers. The result is pushed onto the stack. @@ -316,7 +313,6 @@ def _divu32(ins): return output - def _divi32(ins): ''' Divides 2 32bit signed integers. The result is pushed onto the stack. @@ -346,7 +342,6 @@ def _divi32(ins): return output - def _modu32(ins): ''' Reminder of div. 2 32bit unsigned integers. The result is pushed onto the stack. @@ -362,8 +357,8 @@ def _modu32(ins): output.append('ld hl, 0') output.append('push hl') output.append('push hl') - return output - + return output + rev = is_int(op1) or op1[0] == 't' or op2[0] != 't' output = _32bit_oper(op1, op2, rev) output.append('call __MODU32') @@ -373,7 +368,6 @@ def _modu32(ins): return output - def _modi32(ins): ''' Reminder of div. 2 32bit signed integers. The result is pushed onto the stack. @@ -389,8 +383,8 @@ def _modi32(ins): output.append('ld hl, 0') output.append('push hl') output.append('push hl') - return output - + return output + rev = is_int(op1) or op1[0] == 't' or op2[0] != 't' output = _32bit_oper(op1, op2, rev) output.append('call __MODI32') @@ -400,7 +394,6 @@ def _modi32(ins): return output - def _ltu32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand < 2nd operand (top of the stack). @@ -418,7 +411,6 @@ def _ltu32(ins): return output - def _lti32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand < 2nd operand (top of the stack). @@ -435,7 +427,6 @@ def _lti32(ins): return output - def _gtu32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand > 2nd operand (top of the stack). @@ -457,7 +448,6 @@ def _gtu32(ins): return output - def _gti32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand > 2nd operand (top of the stack). @@ -468,15 +458,14 @@ def _gti32(ins): op1, op2 = tuple(ins.quad[2:]) rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' output = _32bit_oper(op1, op2, rev) - output.append('call __LEI32') # Checks A <= B ? - output.append('sub 1') # Carry if A = 0 (False) - output.append('sbc a, a') # Negates => A > B ? + output.append('call __LEI32') # Checks A <= B ? + output.append('sub 1') # Carry if A = 0 (False) + output.append('sbc a, a') # Negates => A > B ? output.append('push af') REQUIRES.add('lei32.asm') return output - def _leu32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand <= 2nd operand (top of the stack). @@ -492,14 +481,13 @@ def _leu32(ins): output.append('sbc hl, bc') output.append('ex de, hl') output.append('pop de') - output.append('sbc hl, de') # Carry if A > B - output.append('ccf') # Negates result => Carry if A <= B + output.append('sbc hl, de') # Carry if A > B + output.append('ccf') # Negates result => Carry if A <= B output.append('sbc a, a') output.append('push af') return output - def _lei32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand <= 2nd operand (top of the stack). @@ -516,7 +504,6 @@ def _lei32(ins): return output - def _geu32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand >= 2nd operand (top of the stack). @@ -527,15 +514,14 @@ def _geu32(ins): op1, op2 = tuple(ins.quad[2:]) rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' output = _32bit_oper(op1, op2, rev) - output.append('call __SUB32') # Carry if A < B - output.append('ccf') # Negates result => Carry if A >= B + output.append('call __SUB32') # Carry if A < B + output.append('ccf') # Negates result => Carry if A >= B output.append('sbc a, a') output.append('push af') REQUIRES.add('sub32.asm') return output - def _gei32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand >= 2nd operand (top of the stack). @@ -547,14 +533,13 @@ def _gei32(ins): rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' output = _32bit_oper(op1, op2, rev) output.append('call __LTI32') # A = (a < b) - output.append('sub 1') # Carry if !(a < b) - output.append('sbc a, a') # A = !(a < b) = (a >= b) + output.append('sub 1') # Carry if !(a < b) + output.append('sbc a, a') # A = !(a < b) = (a >= b) output.append('push af') REQUIRES.add('lti32.asm') return output - def _eq32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand == 2nd operand (top of the stack). @@ -570,7 +555,6 @@ def _eq32(ins): return output - def _ne32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand != 2nd operand (top of the stack). @@ -581,13 +565,12 @@ def _ne32(ins): op1, op2 = tuple(ins.quad[2:]) output = _32bit_oper(op1, op2) output.append('call __EQ32') - output.append('cpl') # Negates the result + output.append('cpl') # Negates the result output.append('push af') REQUIRES.add('eq32.asm') return output - def _or32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand OR (Logical) 2nd operand (top of the stack). @@ -603,7 +586,6 @@ def _or32(ins): return output - def _bor32(ins): ''' Pops top 2 operands out of the stack, and checks if the 1st operand OR (Bitwise) 2nd operand (top of the stack). @@ -620,7 +602,6 @@ def _bor32(ins): return output - def _xor32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand XOR (Logical) 2nd operand (top of the stack). @@ -636,7 +617,6 @@ def _xor32(ins): return output - def _bxor32(ins): ''' Pops top 2 operands out of the stack, and checks if the 1st operand XOR (Bitwise) 2nd operand (top of the stack). @@ -653,7 +633,6 @@ def _bxor32(ins): return output - def _and32(ins): ''' Compares & pops top 2 operands out of the stack, and checks if the 1st operand AND (Logical) 2nd operand (top of the stack). @@ -665,18 +644,18 @@ def _and32(ins): if _int_ops(op1, op2): op1, op2 = _int_ops(op1, op2) - - if op2 == 0: # X and False = False - if str(op1)[0] == 't': # a temporary term (stack) - output = _32bit_oper(op1) # Remove op1 from the stack + + if op2 == 0: # X and False = False + if str(op1)[0] == 't': # a temporary term (stack) + output = _32bit_oper(op1) # Remove op1 from the stack else: output = [] output.append('xor a') output.append('push af') return output - # For X and TRUE = X we do nothing as we have to convert it to boolean - # which is a rather expensive instruction + # For X and TRUE = X we do nothing as we have to convert it to boolean + # which is a rather expensive instruction output = _32bit_oper(op1, op2) output.append('call __AND32') @@ -685,7 +664,6 @@ def _and32(ins): return output - def _band32(ins): ''' Pops top 2 operands out of the stack, and checks if the 1st operand AND (Bitwise) 2nd operand (top of the stack). @@ -702,7 +680,6 @@ def _band32(ins): return output - def _not32(ins): ''' Negates top (Logical NOT) of the stack (32 bits in DEHL) ''' @@ -713,7 +690,6 @@ def _not32(ins): return output - def _bnot32(ins): ''' Negates top (Bitwise NOT) of the stack (32 bits in DEHL) ''' @@ -725,7 +701,6 @@ def _bnot32(ins): return output - def _neg32(ins): ''' Negates top of the stack (32 bits in DEHL) ''' @@ -737,7 +712,6 @@ def _neg32(ins): return output - def _abs32(ins): ''' Absolute value of top of the stack (32 bits in DEHL) ''' @@ -882,4 +856,3 @@ def _shl32(ins): output.append('push hl') REQUIRES.add('shl32.asm') return output - diff --git a/backend/__8bit.py b/backend/__8bit.py index 80c259dc5..21dfa00b0 100644 --- a/backend/__8bit.py +++ b/backend/__8bit.py @@ -10,7 +10,7 @@ # comparation intermediate-code traductions # -------------------------------------------------------------- -from __common import REQUIRES, is_int, log2, is_2n, _int_ops, tmp_label +from .__common import REQUIRES, is_int, is_2n, _int_ops, tmp_label def int8(op): diff --git a/backend/__array.py b/backend/__array.py index 5f116c7eb..82294fe52 100644 --- a/backend/__array.py +++ b/backend/__array.py @@ -10,10 +10,11 @@ # intermediate-code traductions # -------------------------------------------------------------- -from __common import REQUIRES, is_int -from __f16 import _f16_oper -from __float import _fpush, _float_oper -from errors import InvalidICError + +from .__common import REQUIRES, is_int +from .__f16 import _f16_oper +from .__float import _fpush, _float_oper +from .errors import InvalidICError def _addr(value): diff --git a/backend/__f16.py b/backend/__f16.py index 2cc3eaa17..7e691760f 100644 --- a/backend/__f16.py +++ b/backend/__f16.py @@ -9,9 +9,9 @@ # comparation intermediate-code traductions # -------------------------------------------------------------- -from __common import REQUIRES, is_int, is_float, log2, is_2n, _f_ops -from __32bit import _add32, _sub32, _lti32, _gti32, _gei32, _lei32, _ne32, _eq32 -from __32bit import _and32, _xor32, _or32, _not32, _neg32 +from .__common import REQUIRES, is_int, is_float, _f_ops +from .__32bit import _add32, _sub32, _lti32, _gti32, _gei32, _lei32, _ne32, _eq32 +from .__32bit import _and32, _xor32, _or32, _not32, _neg32 # ----------------------------------------------------- # Fixed Point (16.16) bits operands diff --git a/backend/__float.py b/backend/__float.py index 22c7eeecb..76dbd7ee8 100644 --- a/backend/__float.py +++ b/backend/__float.py @@ -10,7 +10,7 @@ # comparation intermediate-code traductions # -------------------------------------------------------------- -from __common import REQUIRES, is_float, _f_ops +from .__common import REQUIRES, is_float, _f_ops # ----------------------------------------------------- # Floating Point operators diff --git a/backend/__init__.py b/backend/__init__.py index 93c621ee5..c9e1cc9d3 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -5,9 +5,8 @@ import math import re -from api.constants import TYPE -import errors -from errors import InvalidICError as InvalidIC +from . import errors +from .errors import InvalidICError as InvalidIC # Local optimization Flags OPT00 = True @@ -30,85 +29,85 @@ OPT17 = True # 8 bit arithmetic functions -from __8bit import _add8, _sub8, _mul8, _divu8, _divi8, _modu8, _modi8, _neg8, _abs8 +from .__8bit import _add8, _sub8, _mul8, _divu8, _divi8, _modu8, _modi8, _neg8, _abs8 # 8 bit comparison functions -from __8bit import _eq8, _lti8, _ltu8, _gti8, _gtu8, _ne8, _leu8, _lei8, _geu8, _gei8 +from .__8bit import _eq8, _lti8, _ltu8, _gti8, _gtu8, _ne8, _leu8, _lei8, _geu8, _gei8 # 8 bit boolean functions -from __8bit import _or8, _and8, _not8, _xor8, _8bit_oper +from .__8bit import _or8, _and8, _not8, _xor8, _8bit_oper # 8 bit shift operations -from __8bit import _shru8, _shri8, _shl8 +from .__8bit import _shru8, _shri8, _shl8 # 8 bit bitwise operations -from __8bit import _bor8, _band8, _bnot8, _bxor8 +from .__8bit import _bor8, _band8, _bnot8, _bxor8 # 16 bit arithmetic functions -from __16bit import _add16, _sub16, _mul16, _divu16, _divi16, _modu16, _modi16, _neg16, _abs16 +from .__16bit import _add16, _sub16, _mul16, _divu16, _divi16, _modu16, _modi16, _neg16, _abs16 # 16 bit comparison functions -from __16bit import _eq16, _lti16, _ltu16, _gti16, _gtu16, _ne16, _leu16, _lei16, _geu16, _gei16 +from .__16bit import _eq16, _lti16, _ltu16, _gti16, _gtu16, _ne16, _leu16, _lei16, _geu16, _gei16 # 16 bit boolean functions -from __16bit import _or16, _and16, _not16, _xor16, _16bit_oper +from .__16bit import _or16, _and16, _not16, _xor16, _16bit_oper # 16 bit shift operations -from __16bit import _shru16, _shri16, _shl16 +from .__16bit import _shru16, _shri16, _shl16 # 16 bit bitwise operations -from __16bit import _band16, _bor16, _bxor16, _bnot16 +from .__16bit import _band16, _bor16, _bxor16, _bnot16 # 32 bit arithmetic functions -from __32bit import _add32, _sub32, _mul32, _divu32, _divi32, _modu32, _modi32, _neg32, _abs32 +from .__32bit import _add32, _sub32, _mul32, _divu32, _divi32, _modu32, _modi32, _neg32, _abs32 # 32 bit comparison functions -from __32bit import _eq32, _lti32, _ltu32, _gti32, _gtu32, _ne32, _leu32, _lei32, _geu32, _gei32 +from .__32bit import _eq32, _lti32, _ltu32, _gti32, _gtu32, _ne32, _leu32, _lei32, _geu32, _gei32 # 32 bit boolean functions -from __32bit import _or32, _and32, _not32, _xor32, _32bit_oper +from .__32bit import _or32, _and32, _not32, _xor32, _32bit_oper # 32 bit shift operations -from __32bit import _shru32, _shri32, _shl32 +from .__32bit import _shru32, _shri32, _shl32 # 32 bit bitwise operations -from __32bit import _band32, _bor32, _bxor32, _bnot32 +from .__32bit import _band32, _bor32, _bxor32, _bnot32 # Fixed Point arithmetic functions -from __f16 import _addf16, _subf16, _mulf16, _divf16, _modf16, _negf16, _absf16 +from .__f16 import _addf16, _subf16, _mulf16, _divf16, _modf16, _negf16, _absf16 # Fixed Point comparison functions -from __f16 import _eqf16, _ltf16, _gtf16, _nef16, _lef16, _gef16 +from .__f16 import _eqf16, _ltf16, _gtf16, _nef16, _lef16, _gef16 # Fixed Point boolean functions -from __f16 import _orf16, _andf16, _notf16, _xorf16, _f16_oper +from .__f16 import _orf16, _andf16, _notf16, _xorf16, _f16_oper -from __f16 import f16 # Returns DE,HL of a decimal value +from .__f16 import f16 # Returns DE,HL of a decimal value # Floating Point arithmetic functions -from __float import _addf, _subf, _mulf, _divf, _modf, _negf, _powf, _absf +from .__float import _addf, _subf, _mulf, _divf, _modf, _negf, _powf, _absf # Floating Point comparison functions -from __float import _eqf, _ltf, _gtf, _nef, _lef, _gef +from .__float import _eqf, _ltf, _gtf, _nef, _lef, _gef # Floating Point boolean functions -from __float import _orf, _andf, _notf, _xorf, _float_oper, _fpush, _fpop +from .__float import _orf, _andf, _notf, _xorf, _float_oper, _fpush, _fpop # String arithmetic functions -from __str import _addstr +from .__str import _addstr # String comparison functions -from __str import _ltstr, _gtstr, _eqstr, _lestr, _gestr, _nestr, _str_oper, _lenstr +from .__str import _ltstr, _gtstr, _eqstr, _lestr, _gestr, _nestr, _str_oper, _lenstr # Param load and store instructions -from __pload import _pload8, _pload16, _pload32, _ploadf, _ploadstr, _fploadstr -from __pload import _pstore8, _pstore16, _pstore32, _pstoref16, _pstoref, _pstorestr -from __pload import _paddr +from .__pload import _pload8, _pload16, _pload32, _ploadf, _ploadstr, _fploadstr +from .__pload import _pstore8, _pstore16, _pstore32, _pstoref16, _pstoref, _pstorestr +from .__pload import _paddr -from __common import MEMORY, LABEL_COUNTER, TMP_LABELS, TMP_COUNTER, TMP_STORAGES, REQUIRES, INITS -from __common import is_int, is_float, tmp_label +from .__common import MEMORY, LABEL_COUNTER, TMP_LABELS, TMP_COUNTER, TMP_STORAGES, REQUIRES, INITS +from .__common import is_int, is_float, tmp_label # Array store and load instructions -from __array import _aload8, _aload16, _aload32, _aloadf, _aloadstr -from __array import _astore8, _astore16, _astore32, _astoref16, _astoref, _astorestr -from __array import _aaddr +from .__array import _aload8, _aload16, _aload32, _aloadf, _aloadstr +from .__array import _astore8, _astore16, _astore32, _astoref16, _astoref, _astorestr +from .__array import _aaddr # Array store and load instructions -from __parray import _paload8, _paload16, _paload32, _paloadf, _paloadstr -from __parray import _pastore8, _pastore16, _pastore32, _pastoref16, _pastoref, _pastorestr -from __parray import _paaddr +from .__parray import _paload8, _paload16, _paload32, _paloadf, _paloadstr +from .__parray import _pastore8, _pastore16, _pastore32, _pastoref16, _pastoref, _pastorestr +from .__parray import _paaddr # External functions @@ -2088,7 +2087,7 @@ def emmit_start(): output.append('org %s' % OPTIONS.org.value) - if REQUIRES.intersection(MEMINITS) != set([]) or '__MEM_INIT' in INITS: + if REQUIRES.intersection(MEMINITS) or '__MEM_INIT' in INITS: output.append('; Defines HEAP SIZE\n' + OPTIONS.heap_size_label.value + ' EQU ' + str(OPTIONS.heap_size.value)) output.append('%s:' % START_LABEL) @@ -2103,7 +2102,7 @@ def emmit_start(): output.append('ld (%s), hl' % CALL_BACK) output.append('ei') - for x in INITS: + for x in sorted(INITS): output.append('call %s' % x) return output @@ -2134,7 +2133,7 @@ def emmit_end(MEMORY = None): output = [] output.extend(AT_END) - if REQUIRES.intersection(MEMINITS) != set([]) or '__MEM_INIT' in INITS: + if REQUIRES.intersection(MEMINITS) or '__MEM_INIT' in INITS: output.append(OPTIONS.heap_start_label.value + ':') output.append('; Defines DATA END\n' + 'ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ZXBASIC_HEAP_SIZE') else: @@ -2438,7 +2437,7 @@ def output_join(output, new_chunk): else: i += 1 - for i in REQUIRES: + for i in sorted(REQUIRES): output.append('#include once <%s>' % i) return output # Caller will save its contents to a file, or whatever diff --git a/backend/__parray.py b/backend/__parray.py index 2250cc9be..e14aae6a3 100644 --- a/backend/__parray.py +++ b/backend/__parray.py @@ -10,9 +10,10 @@ # comparation intermediate-code traductions # -------------------------------------------------------------- -from __common import REQUIRES -from __float import _fpush -from __f16 import f16 +from .__common import REQUIRES +from .__float import _fpush +from .__f16 import f16 + def _paddr(offset): ''' Generic array address parameter loading. diff --git a/backend/__pload.py b/backend/__pload.py index 90d2d1b79..e044d051c 100644 --- a/backend/__pload.py +++ b/backend/__pload.py @@ -11,12 +11,12 @@ # -------------------------------------------------------------- -from __common import REQUIRES, is_float, is_int -from __8bit import int8, _8bit_oper -from __16bit import int16, _16bit_oper -from __32bit import int32, _32bit_oper -from __f16 import f16, _f16_oper -from __float import _float, _fpop, _fpush, _float_oper +from .__common import REQUIRES, is_int +from .__8bit import int8, _8bit_oper +from .__16bit import int16, _16bit_oper +from .__32bit import _32bit_oper +from .__f16 import _f16_oper +from .__float import _fpush, _float_oper def _paddr(ins): diff --git a/backend/__str.py b/backend/__str.py index 96fc5f5d2..3063c9c25 100644 --- a/backend/__str.py +++ b/backend/__str.py @@ -10,9 +10,10 @@ # comparation intermediate-code traductions # -------------------------------------------------------------- -from __common import REQUIRES +from .__common import REQUIRES -def _str_oper(op1, op2 = None, reversed = False, no_exaf = False): + +def _str_oper(op1, op2=None, reversed=False, no_exaf=False): ''' Returns pop sequence for 16 bits operands 1st operand in HL, 2nd operand in DE @@ -36,11 +37,11 @@ def _str_oper(op1, op2 = None, reversed = False, no_exaf = False): else: indirect = False - if val[0] == '_': # Direct + if val[0] == '_': # Direct output.append('ld de, (%s)' % val) - elif val[0] == '#': # Direct + elif val[0] == '#': # Direct output.append('ld de, %s' % val[1:]) - elif val[0] == '$': # Direct in the stack + elif val[0] == '$': # Direct in the stack output.append('pop de') else: output.append('pop de') @@ -48,7 +49,7 @@ def _str_oper(op1, op2 = None, reversed = False, no_exaf = False): if indirect: output.append('call __LOAD_DE_DE') - REQUIRES.add('lddede.asm') #TODO: This is never used?? + REQUIRES.add('lddede.asm') # TODO: This is never used?? if reversed: tmp = output @@ -62,11 +63,11 @@ def _str_oper(op1, op2 = None, reversed = False, no_exaf = False): else: indirect = False - if val[0] == '_': # Direct + if val[0] == '_': # Direct output.append('ld hl, (%s)' % val) - elif val[0] == '#': # Inmmediate + elif val[0] == '#': # Inmmediate output.append('ld hl, %s' % val[1:]) - elif val[0] == '$': # Direct in the stack + elif val[0] == '$': # Direct in the stack output.append('pop hl') else: output.append('pop hl') @@ -78,28 +79,27 @@ def _str_oper(op1, op2 = None, reversed = False, no_exaf = False): output.append('inc hl') output.append('ld h, (hl)') output.append('ld l, c') - - if reversed: + + if reversed: output.extend(tmp) if not no_exaf: if tmp1 and tmp2: - output.append('ld a, 3') # Marks both strings to be freed + output.append('ld a, 3') # Marks both strings to be freed elif tmp1: - output.append('ld a, 1') # Marks str1 to be freed + output.append('ld a, 1') # Marks str1 to be freed elif tmp2: - output.append('ld a, 2') # Marks str2 to be freed + output.append('ld a, 2') # Marks str2 to be freed else: - output.append('xor a') # Marks no string to be freed - + output.append('xor a') # Marks no string to be freed + if op2 is not None: return (tmp1, tmp2, output) return (tmp1, output) - -def _free_sequence(tmp1, tmp2 = False): +def _free_sequence(tmp1, tmp2=False): ''' Outputs a FREEMEM sequence for 1 or 2 ops ''' if not tmp1 and not tmp2: @@ -122,12 +122,11 @@ def _free_sequence(tmp1, tmp2 = False): return output - def _addstr(ins): ''' Adds 2 string values. The result is pushed onto the stack. Note: This instruction does admit direct strings (as labels). ''' - (tmp1, tmp2, output) = _str_oper(ins.quad[2], ins.quad[3], no_exaf = True) + (tmp1, tmp2, output) = _str_oper(ins.quad[2], ins.quad[3], no_exaf=True) if tmp1: output.append('push hl') @@ -142,7 +141,6 @@ def _addstr(ins): return output - def _ltstr(ins): ''' Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ < b$) @@ -154,7 +152,6 @@ def _ltstr(ins): return output - def _gtstr(ins): ''' Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ > b$) @@ -166,7 +163,6 @@ def _gtstr(ins): return output - def _lestr(ins): ''' Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ <= b$) @@ -178,7 +174,6 @@ def _lestr(ins): return output - def _gestr(ins): ''' Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ >= b$) @@ -190,7 +185,6 @@ def _gestr(ins): return output - def _eqstr(ins): ''' Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ == b$) @@ -202,7 +196,6 @@ def _eqstr(ins): return output - def _nestr(ins): ''' Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ != b$) @@ -217,7 +210,7 @@ def _nestr(ins): def _lenstr(ins): ''' Returns string length ''' - (tmp1, output) = _str_oper(ins.quad[2], no_exaf = True) + (tmp1, output) = _str_oper(ins.quad[2], no_exaf=True) if tmp1: output.append('push hl') @@ -226,4 +219,3 @@ def _lenstr(ins): output.append('push hl') REQUIRES.add('strlen.asm') return output - diff --git a/identityset.py b/identityset.py index b9efe8200..94f1927cc 100644 --- a/identityset.py +++ b/identityset.py @@ -10,23 +10,23 @@ class IdentitySet(object): if they are not exactly the same (same reference) preserving its order (OrderedDict). Allows deleting by ith-index. """ - def __init__(self, L=None): + def __init__(self, l=None): self.elems = [] self._elems = set() - if L is not None: - self.add(L) + if l is not None: + self.add(l) - def add(self, L): - if not isinstance(L, collections.Iterable): - L = [L] - self.elems.extend(x for x in L) - self._elems.update(x for x in L) + def add(self, l): + if not isinstance(l, collections.Iterable): + l = [l] + self.elems.extend(x for x in l if x not in self._elems) + self._elems.update(x for x in l) - def remove(self, L): - if not isinstance(L, collections.Iterable): - L = [L] + def remove(self, l): + if not isinstance(l, collections.Iterable): + l = [l] - self._elems.difference_update(L) + self._elems.difference_update(l) self.elems = [x for x in self.elems if x not in self._elems] def __len__(self): @@ -44,6 +44,12 @@ def __contains__(self, elem): def __delitem__(self, key): self.pop(self.elems.index(key)) + def intersection(self, other): + return IdentitySet([x for x in self.elems if x in self._elems.intersection(other)]) + + def union(self, other): + return IdentitySet(self.elems + [x for x in other]) + def pop(self, i): tmp = self.elems.pop(i) self._elems.remove(tmp) diff --git a/outfmt/__init__.py b/outfmt/__init__.py index b572834d2..fddcf28e6 100644 --- a/outfmt/__init__.py +++ b/outfmt/__init__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from tzx import TZX -from tap import TAP +from .tzx import TZX +from .tap import TAP diff --git a/outfmt/tap.py b/outfmt/tap.py index 1983b3227..0fd1e27a2 100755 --- a/outfmt/tap.py +++ b/outfmt/tap.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # -------------------------------------------- @@ -12,37 +12,36 @@ # Only supports standard headers by now. # -------------------------------------------- -from tzx import TZX +from .tzx import TZX + class TAP(TZX): - ''' Derived from TZX - ''' - def __init__(self): - ''' Initializes the object with standard header - ''' - TZX.__init__(self) - self.output = '' # Restarts the output + """ Derived from TZX. Implements TAP output + """ + def __init__(self): + """Initializes the object with standard header + """ + super(TAP, self).__init__() + self.output = bytearray() # Restarts the output - def standard_block(self, bytes): - ''' Adds a standard block of bytes. For TAP files, it's just the + def standard_block(self, bytes_): + """Adds a standard block of bytes. For TAP files, it's just the Low + Hi byte plus the content (here, the bytes plus the checksum) - ''' - self.out(self.LH(len(bytes) + 1)) # + 1 for CHECKSUM byte + """ + self.out(self.LH(len(bytes_) + 1)) # + 1 for CHECKSUM byte checksum = 0 - for i in bytes: + for i in bytes_: checksum ^= (int(i) & 0xFF) self.out(i) self.out(checksum) - if __name__ == '__main__': - ''' Sample test if invoked from command line - ''' + """Sample test if invoked from command line + """ t = TAP() t.save_code('taptest', 16384, range(255)) t.dump('tzxtest.tap') - diff --git a/outfmt/tzx.py b/outfmt/tzx.py index e9ccb442b..66b9a4d28 100755 --- a/outfmt/tzx.py +++ b/outfmt/tzx.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # -------------------------------------------- @@ -14,58 +14,55 @@ class TZX(object): - ''' Class to represent tzx data - ''' + """ Class to represent tzx data + """ VERSION_MAJOR = 1 VERSION_MINOR = 21 - + # Some interesting constants - + # TZX BLOCK TYPES BLOCK_STANDARD = 0x10 - + # ZX Spectrum BLOCK Types BLOCK_TYPE_HEADER = 0 BLOCK_TYPE_DATA = 0xFF - + # ZX Spectrum BASIC / ARRAY / CODE types HEADER_TYPE_BASIC = 0 HEADER_TYPE_NUMBER_ARRAY = 1 HEADER_TYPE_CHAR_ARRAY = 2 HEADER_TYPE_CODE = 3 - def __init__(self): - ''' Initializes the object with standard header - ''' - self.output = 'ZXTape!' + chr(0x1A) - self.output += chr(self.VERSION_MAJOR) + chr(self.VERSION_MINOR) - + """ Initializes the object with standard header + """ + self.output = bytearray(b'ZXTape!') + self.out(0x1A) + self.out([self.VERSION_MAJOR, self.VERSION_MINOR]) def LH(self, value): - ''' Return a 16 bytes value as a list of 2 bytes [Low, High] - ''' - valueL = value & 0x00FF # Low byte - valueH = (value & 0xFF00) >> 8 # High byte + """ Return a 16 bytes value as a list of 2 bytes [Low, High] + """ + valueL = value & 0x00FF # Low byte + valueH = (value & 0xFF00) >> 8 # High byte return [valueL, valueH] def out(self, l): - ''' Adds a list of bytes to the output string - ''' + """ Adds a list of bytes to the output string + """ if not isinstance(l, list): l = [l] - for i in l: - self.output += chr(int(i) & 0xFF) - + self.output.extend([int(i) & 0xFF for i in l]) def standard_block(self, _bytes): - ''' Adds a standard block of bytes - ''' - self.out(self.BLOCK_STANDARD) # Standard block ID - self.out(self.LH(1000)) # 1000 ms standard pause - self.out(self.LH(len(_bytes) + 1)) # + 1 for CHECKSUM byte + """ Adds a standard block of bytes + """ + self.out(self.BLOCK_STANDARD) # Standard block ID + self.out(self.LH(1000)) # 1000 ms standard pause + self.out(self.LH(len(_bytes) + 1)) # + 1 for CHECKSUM byte checksum = 0 for i in _bytes: @@ -74,15 +71,13 @@ def standard_block(self, _bytes): self.out(checksum) - def dump(self, fname): - ''' Saves TZX file to fname - ''' + """ Saves TZX file to fname + """ open(fname, 'wb').write(self.output) - def save_header(self, _type, title, length, param1, param2): - ''' Saves a generic standard header: + """ Saves a generic standard header: type: 00 -- Program 01 -- Number Array 02 -- Char Array @@ -103,73 +98,42 @@ def save_header(self, _type, title, length, param1, param2): For DATA (02 & 03) NOT USED Info taken from: http://www.worldofspectrum.org/faq/reference/48kreference.htm#TapeDataStructure - ''' - title = (title + 10 * ' ')[:10] # Padd it with spaces - title_bytes = [ord(i) for i in title] # Convert it to bytes + """ + title = (title + 10 * ' ')[:10] # Padd it with spaces + title_bytes = [ord(i) for i in title] # Convert it to bytes _bytes = [self.BLOCK_TYPE_HEADER, _type] + title_bytes + self.LH(length) + self.LH(param1) + self.LH(param2) self.standard_block(_bytes) - def standard_bytes_header(self, title, addr, length): - ''' Generates a standard header block of CODE type - ''' - self.save_header(self.HEADER_TYPE_CODE, title, length, param1 = addr, param2 = 32768) - - - def standard_program_header(self, title, length, line = 32768): - ''' Generates a standard header block of PROGRAM type - ''' - self.save_header(self.HEADER_TYPE_BASIC, title, length, param1 = line, param2 = length) + """ Generates a standard header block of CODE type + """ + self.save_header(self.HEADER_TYPE_CODE, title, length, param1=addr, param2=32768) + def standard_program_header(self, title, length, line=32768): + """ Generates a standard header block of PROGRAM type + """ + self.save_header(self.HEADER_TYPE_BASIC, title, length, param1=line, param2=length) def save_code(self, title, addr, _bytes): - ''' Saves the given bytes as code. If bytes are strings, + """ Saves the given bytes as code. If bytes are strings, its chars will be converted to bytes - ''' + """ self.standard_bytes_header(title, addr, len(_bytes)) - _bytes = [self.BLOCK_TYPE_DATA] + [(int(x) & 0xFF) for x in _bytes] # & 0xFF truncates to bytes + _bytes = [self.BLOCK_TYPE_DATA] + [(int(x) & 0xFF) for x in _bytes] # & 0xFF truncates to bytes self.standard_block(_bytes) - - def save_program(self, title, bytes, line = 32768): - ''' Saves the given bytes as a BASIC program. - ''' + def save_program(self, title, bytes, line=32768): + """ Saves the given bytes as a BASIC program. + """ self.standard_program_header(title, len(bytes), line) - bytes = [self.BLOCK_TYPE_DATA] + [(int(x) & 0xFF) for x in bytes] # & 0xFF truncates to bytes + bytes = [self.BLOCK_TYPE_DATA] + [(int(x) & 0xFF) for x in bytes] # & 0xFF truncates to bytes self.standard_block(bytes) - -class TAP(TZX): - ''' Derived from TZX - ''' - def __init__(self): - ''' Initializes the object with standard header - ''' - TZX.__init__(self) - self.output = '' # Restarts the output - - - def standard_block(self, bytes): - ''' Adds a standard block of bytes. For TAP files, it's just the - Low + Hi byte plus the content (here, the bytes plus the checksum) - ''' - self.out(self.LH(len(bytes) + 1)) # + 1 for CHECKSUM byte - - checksum = 0 - for i in bytes: - checksum ^= (int(i) & 0xFF) - self.out(i) - - self.out(checksum) - - - if __name__ == '__main__': - ''' Sample test if invoked from command line - ''' + """ Sample test if invoked from command line + """ t = TZX() t.save_code('tzxtest', 16384, range(255)) t.dump('tzxtest.tzx') - diff --git a/ply/__init__.py b/ply/__init__.py deleted file mode 100644 index 853a98554..000000000 --- a/ply/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# PLY package -# Author: David Beazley (dave@dabeaz.com) - -__all__ = ['lex','yacc'] diff --git a/ply/cpp.py b/ply/cpp.py deleted file mode 100644 index 39f9d47f3..000000000 --- a/ply/cpp.py +++ /dev/null @@ -1,898 +0,0 @@ -# ----------------------------------------------------------------------------- -# cpp.py -# -# Author: David Beazley (http://www.dabeaz.com) -# Copyright (C) 2007 -# All rights reserved -# -# This module implements an ANSI-C style lexical preprocessor for PLY. -# ----------------------------------------------------------------------------- -from __future__ import generators - -# ----------------------------------------------------------------------------- -# Default preprocessor lexer definitions. These tokens are enough to get -# a basic preprocessor working. Other modules may import these if they want -# ----------------------------------------------------------------------------- - -tokens = ( - 'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT', 'CPP_POUND','CPP_DPOUND' -) - -literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\"" - -# Whitespace -def t_CPP_WS(t): - r'\s+' - t.lexer.lineno += t.value.count("\n") - return t - -t_CPP_POUND = r'\#' -t_CPP_DPOUND = r'\#\#' - -# Identifier -t_CPP_ID = r'[A-Za-z_][\w_]*' - -# Integer literal -def CPP_INTEGER(t): - r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU]|[lL]|[uU][lL]|[lL][uU])?)' - return t - -t_CPP_INTEGER = CPP_INTEGER - -# Floating literal -t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' - -# String literal -def t_CPP_STRING(t): - r'\"([^\\\n]|(\\(.|\n)))*?\"' - t.lexer.lineno += t.value.count("\n") - return t - -# Character constant 'c' or L'c' -def t_CPP_CHAR(t): - r'(L)?\'([^\\\n]|(\\(.|\n)))*?\'' - t.lexer.lineno += t.value.count("\n") - return t - -# Comment -def t_CPP_COMMENT(t): - r'(/\*(.|\n)*?\*/)|(//.*?\n)' - t.lexer.lineno += t.value.count("\n") - return t - -def t_error(t): - t.type = t.value[0] - t.value = t.value[0] - t.lexer.skip(1) - return t - -import re -import copy -import time -import os.path - -# ----------------------------------------------------------------------------- -# trigraph() -# -# Given an input string, this function replaces all trigraph sequences. -# The following mapping is used: -# -# ??= # -# ??/ \ -# ??' ^ -# ??( [ -# ??) ] -# ??! | -# ??< { -# ??> } -# ??- ~ -# ----------------------------------------------------------------------------- - -_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''') -_trigraph_rep = { - '=':'#', - '/':'\\', - "'":'^', - '(':'[', - ')':']', - '!':'|', - '<':'{', - '>':'}', - '-':'~' -} - -def trigraph(input): - return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input) - -# ------------------------------------------------------------------ -# Macro object -# -# This object holds information about preprocessor macros -# -# .name - Macro name (string) -# .value - Macro value (a list of tokens) -# .arglist - List of argument names -# .variadic - Boolean indicating whether or not variadic macro -# .vararg - Name of the variadic parameter -# -# When a macro is created, the macro replacement token sequence is -# pre-scanned and used to create patch lists that are later used -# during macro expansion -# ------------------------------------------------------------------ - -class Macro(object): - def __init__(self,name,value,arglist=None,variadic=False): - self.name = name - self.value = value - self.arglist = arglist - self.variadic = variadic - if variadic: - self.vararg = arglist[-1] - self.source = None - -# ------------------------------------------------------------------ -# Preprocessor object -# -# Object representing a preprocessor. Contains macro definitions, -# include directories, and other information -# ------------------------------------------------------------------ - -class Preprocessor(object): - def __init__(self,lexer=None): - if lexer is None: - lexer = lex.lexer - self.lexer = lexer - self.macros = { } - self.path = [] - self.temp_path = [] - - # Probe the lexer for selected tokens - self.lexprobe() - - tm = time.localtime() - self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm)) - self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm)) - self.parser = None - - # ----------------------------------------------------------------------------- - # tokenize() - # - # Utility function. Given a string of text, tokenize into a list of tokens - # ----------------------------------------------------------------------------- - - def tokenize(self,text): - tokens = [] - self.lexer.input(text) - while True: - tok = self.lexer.token() - if not tok: break - tokens.append(tok) - return tokens - - # --------------------------------------------------------------------- - # error() - # - # Report a preprocessor error/warning of some kind - # ---------------------------------------------------------------------- - - def error(self,file,line,msg): - print >>sys.stderr,"%s:%d %s" % (file,line,msg) - - # ---------------------------------------------------------------------- - # lexprobe() - # - # This method probes the preprocessor lexer object to discover - # the token types of symbols that are important to the preprocessor. - # If this works right, the preprocessor will simply "work" - # with any suitable lexer regardless of how tokens have been named. - # ---------------------------------------------------------------------- - - def lexprobe(self): - - # Determine the token type for identifiers - self.lexer.input("identifier") - tok = self.lexer.token() - if not tok or tok.value != "identifier": - print "Couldn't determine identifier type" - else: - self.t_ID = tok.type - - # Determine the token type for integers - self.lexer.input("12345") - tok = self.lexer.token() - if not tok or int(tok.value) != 12345: - print "Couldn't determine integer type" - else: - self.t_INTEGER = tok.type - self.t_INTEGER_TYPE = type(tok.value) - - # Determine the token type for strings enclosed in double quotes - self.lexer.input("\"filename\"") - tok = self.lexer.token() - if not tok or tok.value != "\"filename\"": - print "Couldn't determine string type" - else: - self.t_STRING = tok.type - - # Determine the token type for whitespace--if any - self.lexer.input(" ") - tok = self.lexer.token() - if not tok or tok.value != " ": - self.t_SPACE = None - else: - self.t_SPACE = tok.type - - # Determine the token type for newlines - self.lexer.input("\n") - tok = self.lexer.token() - if not tok or tok.value != "\n": - self.t_NEWLINE = None - print "Couldn't determine token for newlines" - else: - self.t_NEWLINE = tok.type - - self.t_WS = (self.t_SPACE, self.t_NEWLINE) - - # Check for other characters used by the preprocessor - chars = [ '<','>','#','##','\\','(',')',',','.'] - for c in chars: - self.lexer.input(c) - tok = self.lexer.token() - if not tok or tok.value != c: - print "Unable to lex '%s' required for preprocessor" % c - - # ---------------------------------------------------------------------- - # add_path() - # - # Adds a search path to the preprocessor. - # ---------------------------------------------------------------------- - - def add_path(self,path): - self.path.append(path) - - # ---------------------------------------------------------------------- - # group_lines() - # - # Given an input string, this function splits it into lines. Trailing whitespace - # is removed. Any line ending with \ is grouped with the next line. This - # function forms the lowest level of the preprocessor---grouping into text into - # a line-by-line format. - # ---------------------------------------------------------------------- - - def group_lines(self,input): - lex = self.lexer.clone() - lines = [x.rstrip() for x in input.splitlines()] - for i in xrange(len(lines)): - j = i+1 - while lines[i].endswith('\\') and (j < len(lines)): - lines[i] = lines[i][:-1]+lines[j] - lines[j] = "" - j += 1 - - input = "\n".join(lines) - lex.input(input) - lex.lineno = 1 - - current_line = [] - while True: - tok = lex.token() - if not tok: - break - current_line.append(tok) - if tok.type in self.t_WS and '\n' in tok.value: - yield current_line - current_line = [] - - if current_line: - yield current_line - - # ---------------------------------------------------------------------- - # tokenstrip() - # - # Remove leading/trailing whitespace tokens from a token list - # ---------------------------------------------------------------------- - - def tokenstrip(self,tokens): - i = 0 - while i < len(tokens) and tokens[i].type in self.t_WS: - i += 1 - del tokens[:i] - i = len(tokens)-1 - while i >= 0 and tokens[i].type in self.t_WS: - i -= 1 - del tokens[i+1:] - return tokens - - - # ---------------------------------------------------------------------- - # collect_args() - # - # Collects comma separated arguments from a list of tokens. The arguments - # must be enclosed in parenthesis. Returns a tuple (tokencount,args,positions) - # where tokencount is the number of tokens consumed, args is a list of arguments, - # and positions is a list of integers containing the starting index of each - # argument. Each argument is represented by a list of tokens. - # - # When collecting arguments, leading and trailing whitespace is removed - # from each argument. - # - # This function properly handles nested parenthesis and commas---these do not - # define new arguments. - # ---------------------------------------------------------------------- - - def collect_args(self,tokenlist): - args = [] - positions = [] - current_arg = [] - nesting = 1 - tokenlen = len(tokenlist) - - # Search for the opening '('. - i = 0 - while (i < tokenlen) and (tokenlist[i].type in self.t_WS): - i += 1 - - if (i < tokenlen) and (tokenlist[i].value == '('): - positions.append(i+1) - else: - self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments") - return 0, [], [] - - i += 1 - - while i < tokenlen: - t = tokenlist[i] - if t.value == '(': - current_arg.append(t) - nesting += 1 - elif t.value == ')': - nesting -= 1 - if nesting == 0: - if current_arg: - args.append(self.tokenstrip(current_arg)) - positions.append(i) - return i+1,args,positions - current_arg.append(t) - elif t.value == ',' and nesting == 1: - args.append(self.tokenstrip(current_arg)) - positions.append(i+1) - current_arg = [] - else: - current_arg.append(t) - i += 1 - - # Missing end argument - self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments") - return 0, [],[] - - # ---------------------------------------------------------------------- - # macro_prescan() - # - # Examine the macro value (token sequence) and identify patch points - # This is used to speed up macro expansion later on---we'll know - # right away where to apply patches to the value to form the expansion - # ---------------------------------------------------------------------- - - def macro_prescan(self,macro): - macro.patch = [] # Standard macro arguments - macro.str_patch = [] # String conversion expansion - macro.var_comma_patch = [] # Variadic macro comma patch - i = 0 - while i < len(macro.value): - if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist: - argnum = macro.arglist.index(macro.value[i].value) - # Conversion of argument to a string - if i > 0 and macro.value[i-1].value == '#': - macro.value[i] = copy.copy(macro.value[i]) - macro.value[i].type = self.t_STRING - del macro.value[i-1] - macro.str_patch.append((argnum,i-1)) - continue - # Concatenation - elif (i > 0 and macro.value[i-1].value == '##'): - macro.patch.append(('c',argnum,i-1)) - del macro.value[i-1] - continue - elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'): - macro.patch.append(('c',argnum,i)) - i += 1 - continue - # Standard expansion - else: - macro.patch.append(('e',argnum,i)) - elif macro.value[i].value == '##': - if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \ - ((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \ - (macro.value[i+1].value == macro.vararg): - macro.var_comma_patch.append(i-1) - i += 1 - macro.patch.sort(key=lambda x: x[2],reverse=True) - - # ---------------------------------------------------------------------- - # macro_expand_args() - # - # Given a Macro and list of arguments (each a token list), this method - # returns an expanded version of a macro. The return value is a token sequence - # representing the replacement macro tokens - # ---------------------------------------------------------------------- - - def macro_expand_args(self,macro,args): - # Make a copy of the macro token sequence - rep = [copy.copy(_x) for _x in macro.value] - - # Make string expansion patches. These do not alter the length of the replacement sequence - - str_expansion = {} - for argnum, i in macro.str_patch: - if argnum not in str_expansion: - str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\") - rep[i] = copy.copy(rep[i]) - rep[i].value = str_expansion[argnum] - - # Make the variadic macro comma patch. If the variadic macro argument is empty, we get rid - comma_patch = False - if macro.variadic and not args[-1]: - for i in macro.var_comma_patch: - rep[i] = None - comma_patch = True - - # Make all other patches. The order of these matters. It is assumed that the patch list - # has been sorted in reverse order of patch location since replacements will cause the - # size of the replacement sequence to expand from the patch point. - - expanded = { } - for ptype, argnum, i in macro.patch: - # Concatenation. Argument is left unexpanded - if ptype == 'c': - rep[i:i+1] = args[argnum] - # Normal expansion. Argument is macro expanded first - elif ptype == 'e': - if argnum not in expanded: - expanded[argnum] = self.expand_macros(args[argnum]) - rep[i:i+1] = expanded[argnum] - - # Get rid of removed comma if necessary - if comma_patch: - rep = [_i for _i in rep if _i] - - return rep - - - # ---------------------------------------------------------------------- - # expand_macros() - # - # Given a list of tokens, this function performs macro expansion. - # The expanded argument is a dictionary that contains macros already - # expanded. This is used to prevent infinite recursion. - # ---------------------------------------------------------------------- - - def expand_macros(self,tokens,expanded=None): - if expanded is None: - expanded = {} - i = 0 - while i < len(tokens): - t = tokens[i] - if t.type == self.t_ID: - if t.value in self.macros and t.value not in expanded: - # Yes, we found a macro match - expanded[t.value] = True - - m = self.macros[t.value] - if not m.arglist: - # A simple macro - ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded) - for e in ex: - e.lineno = t.lineno - tokens[i:i+1] = ex - i += len(ex) - else: - # A macro with arguments - j = i + 1 - while j < len(tokens) and tokens[j].type in self.t_WS: - j += 1 - if tokens[j].value == '(': - tokcount,args,positions = self.collect_args(tokens[j:]) - if not m.variadic and len(args) != len(m.arglist): - self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist))) - i = j + tokcount - elif m.variadic and len(args) < len(m.arglist)-1: - if len(m.arglist) > 2: - self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1)) - else: - self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1)) - i = j + tokcount - else: - if m.variadic: - if len(args) == len(m.arglist)-1: - args.append([]) - else: - args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1] - del args[len(m.arglist):] - - # Get macro replacement text - rep = self.macro_expand_args(m,args) - rep = self.expand_macros(rep,expanded) - for r in rep: - r.lineno = t.lineno - tokens[i:j+tokcount] = rep - i += len(rep) - del expanded[t.value] - continue - elif t.value == '__LINE__': - t.type = self.t_INTEGER - t.value = self.t_INTEGER_TYPE(t.lineno) - - i += 1 - return tokens - - # ---------------------------------------------------------------------- - # evalexpr() - # - # Evaluate an expression token sequence for the purposes of evaluating - # integral expressions. - # ---------------------------------------------------------------------- - - def evalexpr(self,tokens): - # tokens = tokenize(line) - # Search for defined macros - i = 0 - while i < len(tokens): - if tokens[i].type == self.t_ID and tokens[i].value == 'defined': - j = i + 1 - needparen = False - result = "0L" - while j < len(tokens): - if tokens[j].type in self.t_WS: - j += 1 - continue - elif tokens[j].type == self.t_ID: - if tokens[j].value in self.macros: - result = "1L" - else: - result = "0L" - if not needparen: break - elif tokens[j].value == '(': - needparen = True - elif tokens[j].value == ')': - break - else: - self.error(self.source,tokens[i].lineno,"Malformed defined()") - j += 1 - tokens[i].type = self.t_INTEGER - tokens[i].value = self.t_INTEGER_TYPE(result) - del tokens[i+1:j+1] - i += 1 - tokens = self.expand_macros(tokens) - for i,t in enumerate(tokens): - if t.type == self.t_ID: - tokens[i] = copy.copy(t) - tokens[i].type = self.t_INTEGER - tokens[i].value = self.t_INTEGER_TYPE("0L") - elif t.type == self.t_INTEGER: - tokens[i] = copy.copy(t) - # Strip off any trailing suffixes - tokens[i].value = str(tokens[i].value) - while tokens[i].value[-1] not in "0123456789abcdefABCDEF": - tokens[i].value = tokens[i].value[:-1] - - expr = "".join([str(x.value) for x in tokens]) - expr = expr.replace("&&"," and ") - expr = expr.replace("||"," or ") - expr = expr.replace("!"," not ") - try: - result = eval(expr) - except StandardError: - self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression") - result = 0 - return result - - # ---------------------------------------------------------------------- - # parsegen() - # - # Parse an input string/ - # ---------------------------------------------------------------------- - def parsegen(self,input,source=None): - - # Replace trigraph sequences - t = trigraph(input) - lines = self.group_lines(t) - - if not source: - source = "" - - self.define("__FILE__ \"%s\"" % source) - - self.source = source - chunk = [] - enable = True - iftrigger = False - ifstack = [] - - for x in lines: - for i,tok in enumerate(x): - if tok.type not in self.t_WS: break - if tok.value == '#': - # Preprocessor directive - - for tok in x: - if tok in self.t_WS and '\n' in tok.value: - chunk.append(tok) - - dirtokens = self.tokenstrip(x[i+1:]) - if dirtokens: - name = dirtokens[0].value - args = self.tokenstrip(dirtokens[1:]) - else: - name = "" - args = [] - - if name == 'define': - if enable: - for tok in self.expand_macros(chunk): - yield tok - chunk = [] - self.define(args) - elif name == 'include': - if enable: - for tok in self.expand_macros(chunk): - yield tok - chunk = [] - oldfile = self.macros['__FILE__'] - for tok in self.include(args): - yield tok - self.macros['__FILE__'] = oldfile - self.source = source - elif name == 'undef': - if enable: - for tok in self.expand_macros(chunk): - yield tok - chunk = [] - self.undef(args) - elif name == 'ifdef': - ifstack.append((enable,iftrigger)) - if enable: - if not args[0].value in self.macros: - enable = False - iftrigger = False - else: - iftrigger = True - elif name == 'ifndef': - ifstack.append((enable,iftrigger)) - if enable: - if args[0].value in self.macros: - enable = False - iftrigger = False - else: - iftrigger = True - elif name == 'if': - ifstack.append((enable,iftrigger)) - if enable: - result = self.evalexpr(args) - if not result: - enable = False - iftrigger = False - else: - iftrigger = True - elif name == 'elif': - if ifstack: - if ifstack[-1][0]: # We only pay attention if outer "if" allows this - if enable: # If already true, we flip enable False - enable = False - elif not iftrigger: # If False, but not triggered yet, we'll check expression - result = self.evalexpr(args) - if result: - enable = True - iftrigger = True - else: - self.error(self.source,dirtokens[0].lineno,"Misplaced #elif") - - elif name == 'else': - if ifstack: - if ifstack[-1][0]: - if enable: - enable = False - elif not iftrigger: - enable = True - iftrigger = True - else: - self.error(self.source,dirtokens[0].lineno,"Misplaced #else") - - elif name == 'endif': - if ifstack: - enable,iftrigger = ifstack.pop() - else: - self.error(self.source,dirtokens[0].lineno,"Misplaced #endif") - else: - # Unknown preprocessor directive - pass - - else: - # Normal text - if enable: - chunk.extend(x) - - for tok in self.expand_macros(chunk): - yield tok - chunk = [] - - # ---------------------------------------------------------------------- - # include() - # - # Implementation of file-inclusion - # ---------------------------------------------------------------------- - - def include(self,tokens): - # Try to extract the filename and then process an include file - if not tokens: - return - if tokens: - if tokens[0].value != '<' and tokens[0].type != self.t_STRING: - tokens = self.expand_macros(tokens) - - if tokens[0].value == '<': - # Include <...> - i = 1 - while i < len(tokens): - if tokens[i].value == '>': - break - i += 1 - else: - print "Malformed #include <...>" - return - filename = "".join([x.value for x in tokens[1:i]]) - path = self.path + [""] + self.temp_path - elif tokens[0].type == self.t_STRING: - filename = tokens[0].value[1:-1] - path = self.temp_path + [""] + self.path - else: - print "Malformed #include statement" - return - for p in path: - iname = os.path.join(p,filename) - try: - data = open(iname,"r").read() - dname = os.path.dirname(iname) - if dname: - self.temp_path.insert(0,dname) - for tok in self.parsegen(data,filename): - yield tok - if dname: - del self.temp_path[0] - break - except IOError,e: - pass - else: - print "Couldn't find '%s'" % filename - - # ---------------------------------------------------------------------- - # define() - # - # Define a new macro - # ---------------------------------------------------------------------- - - def define(self,tokens): - if isinstance(tokens,(str,unicode)): - tokens = self.tokenize(tokens) - - linetok = tokens - try: - name = linetok[0] - if len(linetok) > 1: - mtype = linetok[1] - else: - mtype = None - if not mtype: - m = Macro(name.value,[]) - self.macros[name.value] = m - elif mtype.type in self.t_WS: - # A normal macro - m = Macro(name.value,self.tokenstrip(linetok[2:])) - self.macros[name.value] = m - elif mtype.value == '(': - # A macro with arguments - tokcount, args, positions = self.collect_args(linetok[1:]) - variadic = False - for a in args: - if variadic: - print "No more arguments may follow a variadic argument" - break - astr = "".join([str(_i.value) for _i in a]) - if astr == "...": - variadic = True - a[0].type = self.t_ID - a[0].value = '__VA_ARGS__' - variadic = True - del a[1:] - continue - elif astr[-3:] == "..." and a[0].type == self.t_ID: - variadic = True - del a[1:] - # If, for some reason, "." is part of the identifier, strip off the name for the purposes - # of macro expansion - if a[0].value[-3:] == '...': - a[0].value = a[0].value[:-3] - continue - if len(a) > 1 or a[0].type != self.t_ID: - print "Invalid macro argument" - break - else: - mvalue = self.tokenstrip(linetok[1+tokcount:]) - i = 0 - while i < len(mvalue): - if i+1 < len(mvalue): - if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##': - del mvalue[i] - continue - elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS: - del mvalue[i+1] - i += 1 - m = Macro(name.value,mvalue,[x[0].value for x in args],variadic) - self.macro_prescan(m) - self.macros[name.value] = m - else: - print "Bad macro definition" - except LookupError: - print "Bad macro definition" - - # ---------------------------------------------------------------------- - # undef() - # - # Undefine a macro - # ---------------------------------------------------------------------- - - def undef(self,tokens): - id = tokens[0].value - try: - del self.macros[id] - except LookupError: - pass - - # ---------------------------------------------------------------------- - # parse() - # - # Parse input text. - # ---------------------------------------------------------------------- - def parse(self,input,source=None,ignore={}): - self.ignore = ignore - self.parser = self.parsegen(input,source) - - # ---------------------------------------------------------------------- - # token() - # - # Method to return individual tokens - # ---------------------------------------------------------------------- - def token(self): - try: - while True: - tok = self.parser.next() - if tok.type not in self.ignore: return tok - except StopIteration: - self.parser = None - return None - -if __name__ == '__main__': - import ply.lex as lex - lexer = lex.lex() - - # Run a preprocessor - import sys - f = open(sys.argv[1]) - input = f.read() - - p = Preprocessor(lexer) - p.parse(input,sys.argv[1]) - while True: - tok = p.token() - if not tok: break - print p.source, tok - - - - - - - - - - - diff --git a/ply/lex.py b/ply/lex.py deleted file mode 100644 index 267ec100f..000000000 --- a/ply/lex.py +++ /dev/null @@ -1,1058 +0,0 @@ -# ----------------------------------------------------------------------------- -# ply: lex.py -# -# Copyright (C) 2001-2009, -# David M. Beazley (Dabeaz LLC) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of the David Beazley or Dabeaz LLC may be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------------------------- - -__version__ = "3.3" -__tabversion__ = "3.2" # Version of table file used - -import re, sys, types, copy, os - -# This tuple contains known string types -try: - # Python 2.6 - StringTypes = (types.StringType, types.UnicodeType) -except AttributeError: - # Python 3.0 - StringTypes = (str, bytes) - -# Extract the code attribute of a function. Different implementations -# are for Python 2/3 compatibility. - -if sys.version_info[0] < 3: - def func_code(f): - return f.func_code -else: - def func_code(f): - return f.__code__ - -# This regular expression is used to match valid token names -_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') - -# Exception thrown when invalid token encountered and no default error -# handler is defined. - -class LexError(Exception): - def __init__(self,message,s): - self.args = (message,) - self.text = s - -# Token class. This class is used to represent the tokens produced. -class LexToken(object): - def __str__(self): - return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos) - def __repr__(self): - return str(self) - -# This object is a stand-in for a logging object created by the -# logging module. - -class PlyLogger(object): - def __init__(self,f): - self.f = f - def critical(self,msg,*args,**kwargs): - self.f.write((msg % args) + "\n") - - def warning(self,msg,*args,**kwargs): - self.f.write("WARNING: "+ (msg % args) + "\n") - - def error(self,msg,*args,**kwargs): - self.f.write("ERROR: " + (msg % args) + "\n") - - info = critical - debug = critical - -# Null logger is used when no output is generated. Does nothing. -class NullLogger(object): - def __getattribute__(self,name): - return self - def __call__(self,*args,**kwargs): - return self - -# ----------------------------------------------------------------------------- -# === Lexing Engine === -# -# The following Lexer class implements the lexer runtime. There are only -# a few public methods and attributes: -# -# input() - Store a new string in the lexer -# token() - Get the next token -# clone() - Clone the lexer -# -# lineno - Current line number -# lexpos - Current position in the input string -# ----------------------------------------------------------------------------- - -class Lexer: - def __init__(self): - self.lexre = None # Master regular expression. This is a list of - # tuples (re,findex) where re is a compiled - # regular expression and findex is a list - # mapping regex group numbers to rules - self.lexretext = None # Current regular expression strings - self.lexstatere = {} # Dictionary mapping lexer states to master regexs - self.lexstateretext = {} # Dictionary mapping lexer states to regex strings - self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names - self.lexstate = "INITIAL" # Current lexer state - self.lexstatestack = [] # Stack of lexer states - self.lexstateinfo = None # State information - self.lexstateignore = {} # Dictionary of ignored characters for each state - self.lexstateerrorf = {} # Dictionary of error functions for each state - self.lexreflags = 0 # Optional re compile flags - self.lexdata = None # Actual input data (as a string) - self.lexpos = 0 # Current position in input text - self.lexlen = 0 # Length of the input text - self.lexerrorf = None # Error rule (if any) - self.lextokens = None # List of valid tokens - self.lexignore = "" # Ignored characters - self.lexliterals = "" # Literal characters that can be passed through - self.lexmodule = None # Module - self.lineno = 1 # Current line number - self.lexoptimize = 0 # Optimized mode - - def clone(self,object=None): - c = copy.copy(self) - - # If the object parameter has been supplied, it means we are attaching the - # lexer to a new object. In this case, we have to rebind all methods in - # the lexstatere and lexstateerrorf tables. - - if object: - newtab = { } - for key, ritem in self.lexstatere.items(): - newre = [] - for cre, findex in ritem: - newfindex = [] - for f in findex: - if not f or not f[0]: - newfindex.append(f) - continue - newfindex.append((getattr(object,f[0].__name__),f[1])) - newre.append((cre,newfindex)) - newtab[key] = newre - c.lexstatere = newtab - c.lexstateerrorf = { } - for key, ef in self.lexstateerrorf.items(): - c.lexstateerrorf[key] = getattr(object,ef.__name__) - c.lexmodule = object - return c - - # ------------------------------------------------------------ - # writetab() - Write lexer information to a table file - # ------------------------------------------------------------ - def writetab(self,tabfile,outputdir=""): - if isinstance(tabfile,types.ModuleType): - return - basetabfilename = tabfile.split(".")[-1] - filename = os.path.join(outputdir,basetabfilename)+".py" - tf = open(filename,"w") - tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__)) - tf.write("_tabversion = %s\n" % repr(__version__)) - tf.write("_lextokens = %s\n" % repr(self.lextokens)) - tf.write("_lexreflags = %s\n" % repr(self.lexreflags)) - tf.write("_lexliterals = %s\n" % repr(self.lexliterals)) - tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo)) - - tabre = { } - # Collect all functions in the initial state - initial = self.lexstatere["INITIAL"] - initialfuncs = [] - for part in initial: - for f in part[1]: - if f and f[0]: - initialfuncs.append(f) - - for key, lre in self.lexstatere.items(): - titem = [] - for i in range(len(lre)): - titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i]))) - tabre[key] = titem - - tf.write("_lexstatere = %s\n" % repr(tabre)) - tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore)) - - taberr = { } - for key, ef in self.lexstateerrorf.items(): - if ef: - taberr[key] = ef.__name__ - else: - taberr[key] = None - tf.write("_lexstateerrorf = %s\n" % repr(taberr)) - tf.close() - - # ------------------------------------------------------------ - # readtab() - Read lexer information from a tab file - # ------------------------------------------------------------ - def readtab(self,tabfile,fdict): - if isinstance(tabfile,types.ModuleType): - lextab = tabfile - else: - if sys.version_info[0] < 3: - exec("import %s as lextab" % tabfile) - else: - env = { } - exec("import %s as lextab" % tabfile, env,env) - lextab = env['lextab'] - - if getattr(lextab,"_tabversion","0.0") != __version__: - raise ImportError("Inconsistent PLY version") - - self.lextokens = lextab._lextokens - self.lexreflags = lextab._lexreflags - self.lexliterals = lextab._lexliterals - self.lexstateinfo = lextab._lexstateinfo - self.lexstateignore = lextab._lexstateignore - self.lexstatere = { } - self.lexstateretext = { } - for key,lre in lextab._lexstatere.items(): - titem = [] - txtitem = [] - for i in range(len(lre)): - titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict))) - txtitem.append(lre[i][0]) - self.lexstatere[key] = titem - self.lexstateretext[key] = txtitem - self.lexstateerrorf = { } - for key,ef in lextab._lexstateerrorf.items(): - self.lexstateerrorf[key] = fdict[ef] - self.begin('INITIAL') - - # ------------------------------------------------------------ - # input() - Push a new string into the lexer - # ------------------------------------------------------------ - def input(self,s): - # Pull off the first character to see if s looks like a string - c = s[:1] - if not isinstance(c,StringTypes): - raise ValueError("Expected a string") - self.lexdata = s - self.lexpos = 0 - self.lexlen = len(s) - - # ------------------------------------------------------------ - # begin() - Changes the lexing state - # ------------------------------------------------------------ - def begin(self,state): - if not state in self.lexstatere: - raise ValueError("Undefined state") - self.lexre = self.lexstatere[state] - self.lexretext = self.lexstateretext[state] - self.lexignore = self.lexstateignore.get(state,"") - self.lexerrorf = self.lexstateerrorf.get(state,None) - self.lexstate = state - - # ------------------------------------------------------------ - # push_state() - Changes the lexing state and saves old on stack - # ------------------------------------------------------------ - def push_state(self,state): - self.lexstatestack.append(self.lexstate) - self.begin(state) - - # ------------------------------------------------------------ - # pop_state() - Restores the previous state - # ------------------------------------------------------------ - def pop_state(self): - self.begin(self.lexstatestack.pop()) - - # ------------------------------------------------------------ - # current_state() - Returns the current lexing state - # ------------------------------------------------------------ - def current_state(self): - return self.lexstate - - # ------------------------------------------------------------ - # skip() - Skip ahead n characters - # ------------------------------------------------------------ - def skip(self,n): - self.lexpos += n - - # ------------------------------------------------------------ - # opttoken() - Return the next token from the Lexer - # - # Note: This function has been carefully implemented to be as fast - # as possible. Don't make changes unless you really know what - # you are doing - # ------------------------------------------------------------ - def token(self): - # Make local copies of frequently referenced attributes - lexpos = self.lexpos - lexlen = self.lexlen - lexignore = self.lexignore - lexdata = self.lexdata - - while lexpos < lexlen: - # This code provides some short-circuit code for whitespace, tabs, and other ignored characters - if lexdata[lexpos] in lexignore: - lexpos += 1 - continue - - # Look for a regular expression match - for lexre,lexindexfunc in self.lexre: - m = lexre.match(lexdata,lexpos) - if not m: continue - - # Create a token for return - tok = LexToken() - tok.value = m.group() - tok.lineno = self.lineno - tok.lexpos = lexpos - - i = m.lastindex - func,tok.type = lexindexfunc[i] - - if not func: - # If no token type was set, it's an ignored token - if tok.type: - self.lexpos = m.end() - return tok - else: - lexpos = m.end() - break - - lexpos = m.end() - - # If token is processed by a function, call it - - tok.lexer = self # Set additional attributes useful in token rules - self.lexmatch = m - self.lexpos = lexpos - - newtok = func(tok) - - # Every function must return a token, if nothing, we just move to next token - if not newtok: - lexpos = self.lexpos # This is here in case user has updated lexpos. - lexignore = self.lexignore # This is here in case there was a state change - break - - # Verify type of the token. If not in the token map, raise an error - if not self.lexoptimize: - if not newtok.type in self.lextokens: - raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( - func_code(func).co_filename, func_code(func).co_firstlineno, - func.__name__, newtok.type),lexdata[lexpos:]) - - return newtok - else: - # No match, see if in literals - if lexdata[lexpos] in self.lexliterals: - tok = LexToken() - tok.value = lexdata[lexpos] - tok.lineno = self.lineno - tok.type = tok.value - tok.lexpos = lexpos - self.lexpos = lexpos + 1 - return tok - - # No match. Call t_error() if defined. - if self.lexerrorf: - tok = LexToken() - tok.value = self.lexdata[lexpos:] - tok.lineno = self.lineno - tok.type = "error" - tok.lexer = self - tok.lexpos = lexpos - self.lexpos = lexpos - newtok = self.lexerrorf(tok) - if lexpos == self.lexpos: - # Error method didn't change text position at all. This is an error. - raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) - lexpos = self.lexpos - if not newtok: continue - return newtok - - self.lexpos = lexpos - raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:]) - - self.lexpos = lexpos + 1 - if self.lexdata is None: - raise RuntimeError("No input string given with input()") - return None - - # Iterator interface - def __iter__(self): - return self - - def next(self): - t = self.token() - if t is None: - raise StopIteration - return t - - __next__ = next - -# ----------------------------------------------------------------------------- -# ==== Lex Builder === -# -# The functions and classes below are used to collect lexing information -# and build a Lexer object from it. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# get_caller_module_dict() -# -# This function returns a dictionary containing all of the symbols defined within -# a caller further down the call stack. This is used to get the environment -# associated with the yacc() call if none was provided. -# ----------------------------------------------------------------------------- - -def get_caller_module_dict(levels): - try: - raise RuntimeError - except RuntimeError: - e,b,t = sys.exc_info() - f = t.tb_frame - while levels > 0: - f = f.f_back - levels -= 1 - ldict = f.f_globals.copy() - if f.f_globals != f.f_locals: - ldict.update(f.f_locals) - - return ldict - -# ----------------------------------------------------------------------------- -# _funcs_to_names() -# -# Given a list of regular expression functions, this converts it to a list -# suitable for output to a table file -# ----------------------------------------------------------------------------- - -def _funcs_to_names(funclist,namelist): - result = [] - for f,name in zip(funclist,namelist): - if f and f[0]: - result.append((name, f[1])) - else: - result.append(f) - return result - -# ----------------------------------------------------------------------------- -# _names_to_funcs() -# -# Given a list of regular expression function names, this converts it back to -# functions. -# ----------------------------------------------------------------------------- - -def _names_to_funcs(namelist,fdict): - result = [] - for n in namelist: - if n and n[0]: - result.append((fdict[n[0]],n[1])) - else: - result.append(n) - return result - -# ----------------------------------------------------------------------------- -# _form_master_re() -# -# This function takes a list of all of the regex components and attempts to -# form the master regular expression. Given limitations in the Python re -# module, it may be necessary to break the master regex into separate expressions. -# ----------------------------------------------------------------------------- - -def _form_master_re(relist,reflags,ldict,toknames): - if not relist: return [] - regex = "|".join(relist) - try: - lexre = re.compile(regex,re.VERBOSE | reflags) - - # Build the index to function map for the matching engine - lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1) - lexindexnames = lexindexfunc[:] - - for f,i in lexre.groupindex.items(): - handle = ldict.get(f,None) - if type(handle) in (types.FunctionType, types.MethodType): - lexindexfunc[i] = (handle,toknames[f]) - lexindexnames[i] = f - elif handle is not None: - lexindexnames[i] = f - if f.find("ignore_") > 0: - lexindexfunc[i] = (None,None) - else: - lexindexfunc[i] = (None, toknames[f]) - - return [(lexre,lexindexfunc)],[regex],[lexindexnames] - except Exception: - m = int(len(relist)/2) - if m == 0: m = 1 - llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames) - rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames) - return llist+rlist, lre+rre, lnames+rnames - -# ----------------------------------------------------------------------------- -# def _statetoken(s,names) -# -# Given a declaration name s of the form "t_" and a dictionary whose keys are -# state names, this function returns a tuple (states,tokenname) where states -# is a tuple of state names and tokenname is the name of the token. For example, -# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') -# ----------------------------------------------------------------------------- - -def _statetoken(s,names): - nonstate = 1 - parts = s.split("_") - for i in range(1,len(parts)): - if not parts[i] in names and parts[i] != 'ANY': break - if i > 1: - states = tuple(parts[1:i]) - else: - states = ('INITIAL',) - - if 'ANY' in states: - states = tuple(names) - - tokenname = "_".join(parts[i:]) - return (states,tokenname) - - -# ----------------------------------------------------------------------------- -# LexerReflect() -# -# This class represents information needed to build a lexer as extracted from a -# user's input file. -# ----------------------------------------------------------------------------- -class LexerReflect(object): - def __init__(self,ldict,log=None,reflags=0): - self.ldict = ldict - self.error_func = None - self.tokens = [] - self.reflags = reflags - self.stateinfo = { 'INITIAL' : 'inclusive'} - self.files = {} - self.error = 0 - - if log is None: - self.log = PlyLogger(sys.stderr) - else: - self.log = log - - # Get all of the basic information - def get_all(self): - self.get_tokens() - self.get_literals() - self.get_states() - self.get_rules() - - # Validate all of the information - def validate_all(self): - self.validate_tokens() - self.validate_literals() - self.validate_rules() - return self.error - - # Get the tokens map - def get_tokens(self): - tokens = self.ldict.get("tokens",None) - if not tokens: - self.log.error("No token list is defined") - self.error = 1 - return - - if not isinstance(tokens,(list, tuple)): - self.log.error("tokens must be a list or tuple") - self.error = 1 - return - - if not tokens: - self.log.error("tokens is empty") - self.error = 1 - return - - self.tokens = tokens - - # Validate the tokens - def validate_tokens(self): - terminals = {} - for n in self.tokens: - if not _is_identifier.match(n): - self.log.error("Bad token name '%s'",n) - self.error = 1 - if n in terminals: - self.log.warning("Token '%s' multiply defined", n) - terminals[n] = 1 - - # Get the literals specifier - def get_literals(self): - self.literals = self.ldict.get("literals","") - - # Validate literals - def validate_literals(self): - try: - for c in self.literals: - if not isinstance(c,StringTypes) or len(c) > 1: - self.log.error("Invalid literal %s. Must be a single character", repr(c)) - self.error = 1 - continue - - except TypeError: - self.log.error("Invalid literals specification. literals must be a sequence of characters") - self.error = 1 - - def get_states(self): - self.states = self.ldict.get("states",None) - # Build statemap - if self.states: - if not isinstance(self.states,(tuple,list)): - self.log.error("states must be defined as a tuple or list") - self.error = 1 - else: - for s in self.states: - if not isinstance(s,tuple) or len(s) != 2: - self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s)) - self.error = 1 - continue - name, statetype = s - if not isinstance(name,StringTypes): - self.log.error("State name %s must be a string", repr(name)) - self.error = 1 - continue - if not (statetype == 'inclusive' or statetype == 'exclusive'): - self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name) - self.error = 1 - continue - if name in self.stateinfo: - self.log.error("State '%s' already defined",name) - self.error = 1 - continue - self.stateinfo[name] = statetype - - # Get all of the symbols with a t_ prefix and sort them into various - # categories (functions, strings, error functions, and ignore characters) - - def get_rules(self): - tsymbols = [f for f in self.ldict if f[:2] == 't_' ] - - # Now build up a list of functions and a list of strings - - self.toknames = { } # Mapping of symbols to token names - self.funcsym = { } # Symbols defined as functions - self.strsym = { } # Symbols defined as strings - self.ignore = { } # Ignore strings by state - self.errorf = { } # Error functions by state - - for s in self.stateinfo: - self.funcsym[s] = [] - self.strsym[s] = [] - - if len(tsymbols) == 0: - self.log.error("No rules of the form t_rulename are defined") - self.error = 1 - return - - for f in tsymbols: - t = self.ldict[f] - states, tokname = _statetoken(f,self.stateinfo) - self.toknames[f] = tokname - - if hasattr(t,"__call__"): - if tokname == 'error': - for s in states: - self.errorf[s] = t - elif tokname == 'ignore': - line = func_code(t).co_firstlineno - file = func_code(t).co_filename - self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__) - self.error = 1 - else: - for s in states: - self.funcsym[s].append((f,t)) - elif isinstance(t, StringTypes): - if tokname == 'ignore': - for s in states: - self.ignore[s] = t - if "\\" in t: - self.log.warning("%s contains a literal backslash '\\'",f) - - elif tokname == 'error': - self.log.error("Rule '%s' must be defined as a function", f) - self.error = 1 - else: - for s in states: - self.strsym[s].append((f,t)) - else: - self.log.error("%s not defined as a function or string", f) - self.error = 1 - - # Sort the functions by line number - for f in self.funcsym.values(): - if sys.version_info[0] < 3: - f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno)) - else: - # Python 3.0 - f.sort(key=lambda x: func_code(x[1]).co_firstlineno) - - # Sort the strings by regular expression length - for s in self.strsym.values(): - if sys.version_info[0] < 3: - s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1]))) - else: - # Python 3.0 - s.sort(key=lambda x: len(x[1]),reverse=True) - - # Validate all of the t_rules collected - def validate_rules(self): - for state in self.stateinfo: - # Validate all rules defined by functions - - - - for fname, f in self.funcsym[state]: - line = func_code(f).co_firstlineno - file = func_code(f).co_filename - self.files[file] = 1 - - tokname = self.toknames[fname] - if isinstance(f, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - nargs = func_code(f).co_argcount - if nargs > reqargs: - self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) - self.error = 1 - continue - - if nargs < reqargs: - self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) - self.error = 1 - continue - - if not f.__doc__: - self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__) - self.error = 1 - continue - - try: - c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags) - if c.match(""): - self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__) - self.error = 1 - except re.error: - _etype, e, _etrace = sys.exc_info() - self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e) - if '#' in f.__doc__: - self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__) - self.error = 1 - - # Validate all rules defined by strings - for name,r in self.strsym[state]: - tokname = self.toknames[name] - if tokname == 'error': - self.log.error("Rule '%s' must be defined as a function", name) - self.error = 1 - continue - - if not tokname in self.tokens and tokname.find("ignore_") < 0: - self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname) - self.error = 1 - continue - - try: - c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags) - if (c.match("")): - self.log.error("Regular expression for rule '%s' matches empty string",name) - self.error = 1 - except re.error: - _etype, e, _etrace = sys.exc_info() - self.log.error("Invalid regular expression for rule '%s'. %s",name,e) - if '#' in r: - self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name) - self.error = 1 - - if not self.funcsym[state] and not self.strsym[state]: - self.log.error("No rules defined for state '%s'",state) - self.error = 1 - - # Validate the error function - efunc = self.errorf.get(state,None) - if efunc: - f = efunc - line = func_code(f).co_firstlineno - file = func_code(f).co_filename - self.files[file] = 1 - - if isinstance(f, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - nargs = func_code(f).co_argcount - if nargs > reqargs: - self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) - self.error = 1 - - if nargs < reqargs: - self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) - self.error = 1 - - for f in self.files: - self.validate_file(f) - - - # ----------------------------------------------------------------------------- - # validate_file() - # - # This checks to see if there are duplicated t_rulename() functions or strings - # in the parser input file. This is done using a simple regular expression - # match on each line in the given file. - # ----------------------------------------------------------------------------- - - def validate_file(self,filename): - import os.path - base,ext = os.path.splitext(filename) - if ext != '.py': return # No idea what the file is. Return OK - - try: - f = open(filename) - lines = f.readlines() - f.close() - except IOError: - return # Couldn't find the file. Don't worry about it - - fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') - sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') - - counthash = { } - linen = 1 - for l in lines: - m = fre.match(l) - if not m: - m = sre.match(l) - if m: - name = m.group(1) - prev = counthash.get(name) - if not prev: - counthash[name] = linen - else: - self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev) - self.error = 1 - linen += 1 - -# ----------------------------------------------------------------------------- -# lex(module) -# -# Build all of the regular expression rules from definitions in the supplied module -# ----------------------------------------------------------------------------- -def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None): - global lexer - ldict = None - stateinfo = { 'INITIAL' : 'inclusive'} - lexobj = Lexer() - lexobj.lexoptimize = optimize - global token,input - - if errorlog is None: - errorlog = PlyLogger(sys.stderr) - - if debug: - if debuglog is None: - debuglog = PlyLogger(sys.stderr) - - # Get the module dictionary used for the lexer - if object: module = object - - if module: - _items = [(k,getattr(module,k)) for k in dir(module)] - ldict = dict(_items) - else: - ldict = get_caller_module_dict(2) - - # Collect parser information from the dictionary - linfo = LexerReflect(ldict,log=errorlog,reflags=reflags) - linfo.get_all() - if not optimize: - if linfo.validate_all(): - raise SyntaxError("Can't build lexer") - - if optimize and lextab: - try: - lexobj.readtab(lextab,ldict) - token = lexobj.token - input = lexobj.input - lexer = lexobj - return lexobj - - except ImportError: - pass - - # Dump some basic debugging information - if debug: - debuglog.info("lex: tokens = %r", linfo.tokens) - debuglog.info("lex: literals = %r", linfo.literals) - debuglog.info("lex: states = %r", linfo.stateinfo) - - # Build a dictionary of valid token names - lexobj.lextokens = { } - for n in linfo.tokens: - lexobj.lextokens[n] = 1 - - # Get literals specification - if isinstance(linfo.literals,(list,tuple)): - lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) - else: - lexobj.lexliterals = linfo.literals - - # Get the stateinfo dictionary - stateinfo = linfo.stateinfo - - regexs = { } - # Build the master regular expressions - for state in stateinfo: - regex_list = [] - - # Add rules defined by functions first - for fname, f in linfo.funcsym[state]: - line = func_code(f).co_firstlineno - file = func_code(f).co_filename - regex_list.append("(?P<%s>%s)" % (fname,f.__doc__)) - if debug: - debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state) - - # Now add all of the simple rules - for name,r in linfo.strsym[state]: - regex_list.append("(?P<%s>%s)" % (name,r)) - if debug: - debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state) - - regexs[state] = regex_list - - # Build the master regular expressions - - if debug: - debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====") - - for state in regexs: - lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames) - lexobj.lexstatere[state] = lexre - lexobj.lexstateretext[state] = re_text - lexobj.lexstaterenames[state] = re_names - if debug: - for i in range(len(re_text)): - debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i]) - - # For inclusive states, we need to add the regular expressions from the INITIAL state - for state,stype in stateinfo.items(): - if state != "INITIAL" and stype == 'inclusive': - lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) - lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) - lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) - - lexobj.lexstateinfo = stateinfo - lexobj.lexre = lexobj.lexstatere["INITIAL"] - lexobj.lexretext = lexobj.lexstateretext["INITIAL"] - lexobj.lexreflags = reflags - - # Set up ignore variables - lexobj.lexstateignore = linfo.ignore - lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","") - - # Set up error functions - lexobj.lexstateerrorf = linfo.errorf - lexobj.lexerrorf = linfo.errorf.get("INITIAL",None) - if not lexobj.lexerrorf: - errorlog.warning("No t_error rule is defined") - - # Check state information for ignore and error rules - for s,stype in stateinfo.items(): - if stype == 'exclusive': - if not s in linfo.errorf: - errorlog.warning("No error rule is defined for exclusive state '%s'", s) - if not s in linfo.ignore and lexobj.lexignore: - errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) - elif stype == 'inclusive': - if not s in linfo.errorf: - linfo.errorf[s] = linfo.errorf.get("INITIAL",None) - if not s in linfo.ignore: - linfo.ignore[s] = linfo.ignore.get("INITIAL","") - - # Create global versions of the token() and input() functions - token = lexobj.token - input = lexobj.input - lexer = lexobj - - # If in optimize mode, we write the lextab - if lextab and optimize: - lexobj.writetab(lextab,outputdir) - - return lexobj - -# ----------------------------------------------------------------------------- -# runmain() -# -# This runs the lexer as a main program -# ----------------------------------------------------------------------------- - -def runmain(lexer=None,data=None): - if not data: - try: - filename = sys.argv[1] - f = open(filename) - data = f.read() - f.close() - except IndexError: - sys.stdout.write("Reading from standard input (type EOF to end):\n") - data = sys.stdin.read() - - if lexer: - _input = lexer.input - else: - _input = input - _input(data) - if lexer: - _token = lexer.token - else: - _token = token - - while 1: - tok = _token() - if not tok: break - sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos)) - -# ----------------------------------------------------------------------------- -# @TOKEN(regex) -# -# This decorator function can be used to set the regex expression on a function -# when its docstring might need to be set in an alternative way -# ----------------------------------------------------------------------------- - -def TOKEN(r): - def set_doc(f): - if hasattr(r,"__call__"): - f.__doc__ = r.__doc__ - else: - f.__doc__ = r - return f - return set_doc - -# Alternative spelling of the TOKEN decorator -Token = TOKEN - diff --git a/ply/yacc.py b/ply/yacc.py deleted file mode 100644 index e9f5c6575..000000000 --- a/ply/yacc.py +++ /dev/null @@ -1,3276 +0,0 @@ -# ----------------------------------------------------------------------------- -# ply: yacc.py -# -# Copyright (C) 2001-2009, -# David M. Beazley (Dabeaz LLC) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of the David Beazley or Dabeaz LLC may be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------------------------- -# -# This implements an LR parser that is constructed from grammar rules defined -# as Python functions. The grammer is specified by supplying the BNF inside -# Python documentation strings. The inspiration for this technique was borrowed -# from John Aycock's Spark parsing system. PLY might be viewed as cross between -# Spark and the GNU bison utility. -# -# The current implementation is only somewhat object-oriented. The -# LR parser itself is defined in terms of an object (which allows multiple -# parsers to co-exist). However, most of the variables used during table -# construction are defined in terms of global variables. Users shouldn't -# notice unless they are trying to define multiple parsers at the same -# time using threads (in which case they should have their head examined). -# -# This implementation supports both SLR and LALR(1) parsing. LALR(1) -# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), -# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, -# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced -# by the more efficient DeRemer and Pennello algorithm. -# -# :::::::: WARNING ::::::: -# -# Construction of LR parsing tables is fairly complicated and expensive. -# To make this module run fast, a *LOT* of work has been put into -# optimization---often at the expensive of readability and what might -# consider to be good Python "coding style." Modify the code at your -# own risk! -# ---------------------------------------------------------------------------- - -__version__ = "3.3" -__tabversion__ = "3.2" # Table version - -#----------------------------------------------------------------------------- -# === User configurable parameters === -# -# Change these to modify the default behavior of yacc (if you wish) -#----------------------------------------------------------------------------- - -yaccdebug = 1 # Debugging mode. If set, yacc generates a - # a 'parser.out' file in the current directory - -debug_file = 'parser.out' # Default name of the debugging file -tab_module = 'parsetab' # Default name of the table module -default_lr = 'LALR' # Default LR table generation method - -error_count = 3 # Number of symbols that must be shifted to leave recovery mode - -yaccdevel = 0 # Set to True if developing yacc. This turns off optimized - # implementations of certain functions. - -resultlimit = 40 # Size limit of results when running in debug mode. - -pickle_protocol = 0 # Protocol to use when writing pickle files - -import re, types, sys, os.path - -# Compatibility function for python 2.6/3.0 -if sys.version_info[0] < 3: - def func_code(f): - return f.func_code -else: - def func_code(f): - return f.__code__ - -# Compatibility -try: - MAXINT = sys.maxint -except AttributeError: - MAXINT = sys.maxsize - -# Python 2.x/3.0 compatibility. -def load_ply_lex(): - if sys.version_info[0] < 3: - import lex - else: - import ply.lex as lex - return lex - -# This object is a stand-in for a logging object created by the -# logging module. PLY will use this by default to create things -# such as the parser.out file. If a user wants more detailed -# information, they can create their own logging object and pass -# it into PLY. - -class PlyLogger(object): - def __init__(self,f): - self.f = f - def debug(self,msg,*args,**kwargs): - self.f.write((msg % args) + "\n") - info = debug - - def warning(self,msg,*args,**kwargs): - self.f.write("WARNING: "+ (msg % args) + "\n") - - def error(self,msg,*args,**kwargs): - self.f.write("ERROR: " + (msg % args) + "\n") - - critical = debug - -# Null logger is used when no output is generated. Does nothing. -class NullLogger(object): - def __getattribute__(self,name): - return self - def __call__(self,*args,**kwargs): - return self - -# Exception raised for yacc-related errors -class YaccError(Exception): pass - -# Format the result message that the parser produces when running in debug mode. -def format_result(r): - repr_str = repr(r) - if '\n' in repr_str: repr_str = repr(repr_str) - if len(repr_str) > resultlimit: - repr_str = repr_str[:resultlimit]+" ..." - result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str) - return result - - -# Format stack entries when the parser is running in debug mode -def format_stack_entry(r): - repr_str = repr(r) - if '\n' in repr_str: repr_str = repr(repr_str) - if len(repr_str) < 16: - return repr_str - else: - return "<%s @ 0x%x>" % (type(r).__name__,id(r)) - -#----------------------------------------------------------------------------- -# === LR Parsing Engine === -# -# The following classes are used for the LR parser itself. These are not -# used during table construction and are independent of the actual LR -# table generation algorithm -#----------------------------------------------------------------------------- - -# This class is used to hold non-terminal grammar symbols during parsing. -# It normally has the following attributes set: -# .type = Grammar symbol type -# .value = Symbol value -# .lineno = Starting line number -# .endlineno = Ending line number (optional, set automatically) -# .lexpos = Starting lex position -# .endlexpos = Ending lex position (optional, set automatically) - -class YaccSymbol: - def __str__(self): return self.type - def __repr__(self): return str(self) - -# This class is a wrapper around the objects actually passed to each -# grammar rule. Index lookup and assignment actually assign the -# .value attribute of the underlying YaccSymbol object. -# The lineno() method returns the line number of a given -# item (or 0 if not defined). The linespan() method returns -# a tuple of (startline,endline) representing the range of lines -# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) -# representing the range of positional information for a symbol. - -class YaccProduction: - def __init__(self,s,stack=None): - self.slice = s - self.stack = stack - self.lexer = None - self.parser= None - def __getitem__(self,n): - if n >= 0: return self.slice[n].value - else: return self.stack[n].value - - def __setitem__(self,n,v): - self.slice[n].value = v - - def __getslice__(self,i,j): - return [s.value for s in self.slice[i:j]] - - def __len__(self): - return len(self.slice) - - def lineno(self,n): - return getattr(self.slice[n],"lineno",0) - - def set_lineno(self,n,lineno): - self.slice[n].lineno = lineno - - def linespan(self,n): - startline = getattr(self.slice[n],"lineno",0) - endline = getattr(self.slice[n],"endlineno",startline) - return startline,endline - - def lexpos(self,n): - return getattr(self.slice[n],"lexpos",0) - - def lexspan(self,n): - startpos = getattr(self.slice[n],"lexpos",0) - endpos = getattr(self.slice[n],"endlexpos",startpos) - return startpos,endpos - - def error(self): - raise SyntaxError - - -# ----------------------------------------------------------------------------- -# == LRParser == -# -# The LR Parsing engine. -# ----------------------------------------------------------------------------- - -class LRParser: - def __init__(self,lrtab,errorf): - self.productions = lrtab.lr_productions - self.action = lrtab.lr_action - self.goto = lrtab.lr_goto - self.errorfunc = errorf - - def errok(self): - self.errorok = 1 - - def restart(self): - del self.statestack[:] - del self.symstack[:] - sym = YaccSymbol() - sym.type = '$end' - self.symstack.append(sym) - self.statestack.append(0) - - def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): - if debug or yaccdevel: - if isinstance(debug,int): - debug = PlyLogger(sys.stderr) - return self.parsedebug(input,lexer,debug,tracking,tokenfunc) - elif tracking: - return self.parseopt(input,lexer,debug,tracking,tokenfunc) - else: - return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc) - - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # parsedebug(). - # - # This is the debugging enabled version of parse(). All changes made to the - # parsing engine should be made here. For the non-debugging version, - # copy this code to a method parseopt() and delete all of the sections - # enclosed in: - # - # #--! DEBUG - # statements - # #--! DEBUG - # - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None): - lookahead = None # Current lookahead symbol - lookaheadstack = [ ] # Stack of lookahead symbols - actions = self.action # Local reference to action table (to avoid lookup on self.) - goto = self.goto # Local reference to goto table (to avoid lookup on self.) - prod = self.productions # Local reference to production list (to avoid lookup on self.) - pslice = YaccProduction(None) # Production object passed to grammar rules - errorcount = 0 # Used during error recovery - - # --! DEBUG - debug.info("PLY: PARSE DEBUG START") - # --! DEBUG - - # If no lexer was given, we will try to use the lex module - if not lexer: - lex = load_ply_lex() - lexer = lex.lexer - - # Set up the lexer and parser objects on pslice - pslice.lexer = lexer - pslice.parser = self - - # If input was supplied, pass to lexer - if input is not None: - lexer.input(input) - - if tokenfunc is None: - # Tokenize function - get_token = lexer.token - else: - get_token = tokenfunc - - # Set up the state and symbol stacks - - statestack = [ ] # Stack of parsing states - self.statestack = statestack - symstack = [ ] # Stack of grammar symbols - self.symstack = symstack - - pslice.stack = symstack # Put in the production - errtoken = None # Err token - - # The start state is assumed to be (0,$end) - - statestack.append(0) - sym = YaccSymbol() - sym.type = "$end" - symstack.append(sym) - state = 0 - while 1: - # Get the next symbol on the input. If a lookahead symbol - # is already set, we just use that. Otherwise, we'll pull - # the next token off of the lookaheadstack or from the lexer - - # --! DEBUG - debug.debug('') - debug.debug('State : %s', state) - # --! DEBUG - - if not lookahead: - if not lookaheadstack: - lookahead = get_token() # Get the next token - else: - lookahead = lookaheadstack.pop() - if not lookahead: - lookahead = YaccSymbol() - lookahead.type = "$end" - - # --! DEBUG - debug.debug('Stack : %s', - ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) - # --! DEBUG - - # Check the action table - ltype = lookahead.type - t = actions[state].get(ltype) - - if t is not None: - if t > 0: - # shift a symbol on the stack - statestack.append(t) - state = t - - # --! DEBUG - debug.debug("Action : Shift and goto state %s", t) - # --! DEBUG - - symstack.append(lookahead) - lookahead = None - - # Decrease error count on successful shift - if errorcount: errorcount -=1 - continue - - if t < 0: - # reduce a symbol on the stack, emit a production - p = prod[-t] - pname = p.name - plen = p.len - - # Get production function - sym = YaccSymbol() - sym.type = pname # Production name - sym.value = None - - # --! DEBUG - if plen: - debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t) - else: - debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t) - - # --! DEBUG - - if plen: - targ = symstack[-plen-1:] - targ[0] = sym - - # --! TRACKING - if tracking: - t1 = targ[1] - sym.lineno = t1.lineno - sym.lexpos = t1.lexpos - t1 = targ[-1] - sym.endlineno = getattr(t1,"endlineno",t1.lineno) - sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) - - # --! TRACKING - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # below as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - del symstack[-plen:] - del statestack[-plen:] - p.callable(pslice) - # --! DEBUG - debug.info("Result : %s", format_result(pslice[0])) - # --! DEBUG - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) - symstack.pop() - statestack.pop() - state = statestack[-1] - sym.type = 'error' - lookahead = sym - errorcount = error_count - self.errorok = 0 - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - else: - - # --! TRACKING - if tracking: - sym.lineno = lexer.lineno - sym.lexpos = lexer.lexpos - # --! TRACKING - - targ = [ sym ] - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # above as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - p.callable(pslice) - # --! DEBUG - debug.info("Result : %s", format_result(pslice[0])) - # --! DEBUG - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) - symstack.pop() - statestack.pop() - state = statestack[-1] - sym.type = 'error' - lookahead = sym - errorcount = error_count - self.errorok = 0 - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - if t == 0: - n = symstack[-1] - result = getattr(n,"value",None) - # --! DEBUG - debug.info("Done : Returning %s", format_result(result)) - debug.info("PLY: PARSE DEBUG END") - # --! DEBUG - return result - - if t == None: - - # --! DEBUG - debug.error('Error : %s', - ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) - # --! DEBUG - - # We have some kind of parsing error here. To handle - # this, we are going to push the current token onto - # the tokenstack and replace it with an 'error' token. - # If there are any synchronization rules, they may - # catch it. - # - # In addition to pushing the error token, we call call - # the user defined p_error() function if this is the - # first syntax error. This function is only called if - # errorcount == 0. - if errorcount == 0 or self.errorok: - errorcount = error_count - self.errorok = 0 - errtoken = lookahead - if errtoken.type == "$end": - errtoken = None # End of file! - if self.errorfunc: - global errok,token,restart - errok = self.errok # Set some special functions available in error recovery - token = get_token - restart = self.restart - if errtoken and not hasattr(errtoken,'lexer'): - errtoken.lexer = lexer - tok = self.errorfunc(errtoken) - del errok, token, restart # Delete special functions - - if self.errorok: - # User must have done some kind of panic - # mode recovery on their own. The - # returned token is the next lookahead - lookahead = tok - errtoken = None - continue - else: - if errtoken: - if hasattr(errtoken,"lineno"): lineno = lookahead.lineno - else: lineno = 0 - if lineno: - sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) - else: - sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) - else: - sys.stderr.write("yacc: Parse error in input. EOF\n") - return - - else: - errorcount = error_count - - # case 1: the statestack only has 1 entry on it. If we're in this state, the - # entire parse has been rolled back and we're completely hosed. The token is - # discarded and we just keep going. - - if len(statestack) <= 1 and lookahead.type != "$end": - lookahead = None - errtoken = None - state = 0 - # Nuke the pushback stack - del lookaheadstack[:] - continue - - # case 2: the statestack has a couple of entries on it, but we're - # at the end of the file. nuke the top entry and generate an error token - - # Start nuking entries on the stack - if lookahead.type == "$end": - # Whoa. We're really hosed here. Bail out - return - - if lookahead.type != 'error': - sym = symstack[-1] - if sym.type == 'error': - # Hmmm. Error is on top of stack, we'll just nuke input - # symbol and continue - lookahead = None - continue - t = YaccSymbol() - t.type = 'error' - if hasattr(lookahead,"lineno"): - t.lineno = lookahead.lineno - t.value = lookahead - lookaheadstack.append(lookahead) - lookahead = t - else: - symstack.pop() - statestack.pop() - state = statestack[-1] # Potential bug fix - - continue - - # Call an error function here - raise RuntimeError("yacc: internal parser error!!!\n") - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # parseopt(). - # - # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY. - # Edit the debug version above, then copy any modifications to the method - # below while removing #--! DEBUG sections. - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - - def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): - lookahead = None # Current lookahead symbol - lookaheadstack = [ ] # Stack of lookahead symbols - actions = self.action # Local reference to action table (to avoid lookup on self.) - goto = self.goto # Local reference to goto table (to avoid lookup on self.) - prod = self.productions # Local reference to production list (to avoid lookup on self.) - pslice = YaccProduction(None) # Production object passed to grammar rules - errorcount = 0 # Used during error recovery - - # If no lexer was given, we will try to use the lex module - if not lexer: - lex = load_ply_lex() - lexer = lex.lexer - - # Set up the lexer and parser objects on pslice - pslice.lexer = lexer - pslice.parser = self - - # If input was supplied, pass to lexer - if input is not None: - lexer.input(input) - - if tokenfunc is None: - # Tokenize function - get_token = lexer.token - else: - get_token = tokenfunc - - # Set up the state and symbol stacks - - statestack = [ ] # Stack of parsing states - self.statestack = statestack - symstack = [ ] # Stack of grammar symbols - self.symstack = symstack - - pslice.stack = symstack # Put in the production - errtoken = None # Err token - - # The start state is assumed to be (0,$end) - - statestack.append(0) - sym = YaccSymbol() - sym.type = '$end' - symstack.append(sym) - state = 0 - while 1: - # Get the next symbol on the input. If a lookahead symbol - # is already set, we just use that. Otherwise, we'll pull - # the next token off of the lookaheadstack or from the lexer - - if not lookahead: - if not lookaheadstack: - lookahead = get_token() # Get the next token - else: - lookahead = lookaheadstack.pop() - if not lookahead: - lookahead = YaccSymbol() - lookahead.type = '$end' - - # Check the action table - ltype = lookahead.type - t = actions[state].get(ltype) - - if t is not None: - if t > 0: - # shift a symbol on the stack - statestack.append(t) - state = t - - symstack.append(lookahead) - lookahead = None - - # Decrease error count on successful shift - if errorcount: errorcount -=1 - continue - - if t < 0: - # reduce a symbol on the stack, emit a production - p = prod[-t] - pname = p.name - plen = p.len - - # Get production function - sym = YaccSymbol() - sym.type = pname # Production name - sym.value = None - - if plen: - targ = symstack[-plen-1:] - targ[0] = sym - - # --! TRACKING - if tracking: - t1 = targ[1] - sym.lineno = t1.lineno - sym.lexpos = t1.lexpos - t1 = targ[-1] - sym.endlineno = getattr(t1,"endlineno",t1.lineno) - sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) - - # --! TRACKING - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # below as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - del symstack[-plen:] - del statestack[-plen:] - p.callable(pslice) - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) - symstack.pop() - statestack.pop() - state = statestack[-1] - sym.type = 'error' - lookahead = sym - errorcount = error_count - self.errorok = 0 - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - else: - - # --! TRACKING - if tracking: - sym.lineno = lexer.lineno - sym.lexpos = lexer.lexpos - # --! TRACKING - - targ = [ sym ] - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # above as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - p.callable(pslice) - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) - symstack.pop() - statestack.pop() - state = statestack[-1] - sym.type = 'error' - lookahead = sym - errorcount = error_count - self.errorok = 0 - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - if t == 0: - n = symstack[-1] - return getattr(n,"value",None) - - if t == None: - - # We have some kind of parsing error here. To handle - # this, we are going to push the current token onto - # the tokenstack and replace it with an 'error' token. - # If there are any synchronization rules, they may - # catch it. - # - # In addition to pushing the error token, we call call - # the user defined p_error() function if this is the - # first syntax error. This function is only called if - # errorcount == 0. - if errorcount == 0 or self.errorok: - errorcount = error_count - self.errorok = 0 - errtoken = lookahead - if errtoken.type == '$end': - errtoken = None # End of file! - if self.errorfunc: - global errok,token,restart - errok = self.errok # Set some special functions available in error recovery - token = get_token - restart = self.restart - if errtoken and not hasattr(errtoken,'lexer'): - errtoken.lexer = lexer - tok = self.errorfunc(errtoken) - del errok, token, restart # Delete special functions - - if self.errorok: - # User must have done some kind of panic - # mode recovery on their own. The - # returned token is the next lookahead - lookahead = tok - errtoken = None - continue - else: - if errtoken: - if hasattr(errtoken,"lineno"): lineno = lookahead.lineno - else: lineno = 0 - if lineno: - sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) - else: - sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) - else: - sys.stderr.write("yacc: Parse error in input. EOF\n") - return - - else: - errorcount = error_count - - # case 1: the statestack only has 1 entry on it. If we're in this state, the - # entire parse has been rolled back and we're completely hosed. The token is - # discarded and we just keep going. - - if len(statestack) <= 1 and lookahead.type != '$end': - lookahead = None - errtoken = None - state = 0 - # Nuke the pushback stack - del lookaheadstack[:] - continue - - # case 2: the statestack has a couple of entries on it, but we're - # at the end of the file. nuke the top entry and generate an error token - - # Start nuking entries on the stack - if lookahead.type == '$end': - # Whoa. We're really hosed here. Bail out - return - - if lookahead.type != 'error': - sym = symstack[-1] - if sym.type == 'error': - # Hmmm. Error is on top of stack, we'll just nuke input - # symbol and continue - lookahead = None - continue - t = YaccSymbol() - t.type = 'error' - if hasattr(lookahead,"lineno"): - t.lineno = lookahead.lineno - t.value = lookahead - lookaheadstack.append(lookahead) - lookahead = t - else: - symstack.pop() - statestack.pop() - state = statestack[-1] # Potential bug fix - - continue - - # Call an error function here - raise RuntimeError("yacc: internal parser error!!!\n") - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # parseopt_notrack(). - # - # Optimized version of parseopt() with line number tracking removed. - # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove - # code in the #--! TRACKING sections - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): - lookahead = None # Current lookahead symbol - lookaheadstack = [ ] # Stack of lookahead symbols - actions = self.action # Local reference to action table (to avoid lookup on self.) - goto = self.goto # Local reference to goto table (to avoid lookup on self.) - prod = self.productions # Local reference to production list (to avoid lookup on self.) - pslice = YaccProduction(None) # Production object passed to grammar rules - errorcount = 0 # Used during error recovery - - # If no lexer was given, we will try to use the lex module - if not lexer: - lex = load_ply_lex() - lexer = lex.lexer - - # Set up the lexer and parser objects on pslice - pslice.lexer = lexer - pslice.parser = self - - # If input was supplied, pass to lexer - if input is not None: - lexer.input(input) - - if tokenfunc is None: - # Tokenize function - get_token = lexer.token - else: - get_token = tokenfunc - - # Set up the state and symbol stacks - - statestack = [ ] # Stack of parsing states - self.statestack = statestack - symstack = [ ] # Stack of grammar symbols - self.symstack = symstack - - pslice.stack = symstack # Put in the production - errtoken = None # Err token - - # The start state is assumed to be (0,$end) - - statestack.append(0) - sym = YaccSymbol() - sym.type = '$end' - symstack.append(sym) - state = 0 - while 1: - # Get the next symbol on the input. If a lookahead symbol - # is already set, we just use that. Otherwise, we'll pull - # the next token off of the lookaheadstack or from the lexer - - if not lookahead: - if not lookaheadstack: - lookahead = get_token() # Get the next token - else: - lookahead = lookaheadstack.pop() - if not lookahead: - lookahead = YaccSymbol() - lookahead.type = '$end' - - # Check the action table - ltype = lookahead.type - t = actions[state].get(ltype) - - if t is not None: - if t > 0: - # shift a symbol on the stack - statestack.append(t) - state = t - - symstack.append(lookahead) - lookahead = None - - # Decrease error count on successful shift - if errorcount: errorcount -=1 - continue - - if t < 0: - # reduce a symbol on the stack, emit a production - p = prod[-t] - pname = p.name - plen = p.len - - # Get production function - sym = YaccSymbol() - sym.type = pname # Production name - sym.value = None - - if plen: - targ = symstack[-plen-1:] - targ[0] = sym - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # below as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - del symstack[-plen:] - del statestack[-plen:] - p.callable(pslice) - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) - symstack.pop() - statestack.pop() - state = statestack[-1] - sym.type = 'error' - lookahead = sym - errorcount = error_count - self.errorok = 0 - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - else: - - targ = [ sym ] - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # above as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - p.callable(pslice) - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) - symstack.pop() - statestack.pop() - state = statestack[-1] - sym.type = 'error' - lookahead = sym - errorcount = error_count - self.errorok = 0 - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - if t == 0: - n = symstack[-1] - return getattr(n,"value",None) - - if t == None: - - # We have some kind of parsing error here. To handle - # this, we are going to push the current token onto - # the tokenstack and replace it with an 'error' token. - # If there are any synchronization rules, they may - # catch it. - # - # In addition to pushing the error token, we call call - # the user defined p_error() function if this is the - # first syntax error. This function is only called if - # errorcount == 0. - if errorcount == 0 or self.errorok: - errorcount = error_count - self.errorok = 0 - errtoken = lookahead - if errtoken.type == '$end': - errtoken = None # End of file! - if self.errorfunc: - global errok,token,restart - errok = self.errok # Set some special functions available in error recovery - token = get_token - restart = self.restart - if errtoken and not hasattr(errtoken,'lexer'): - errtoken.lexer = lexer - tok = self.errorfunc(errtoken) - del errok, token, restart # Delete special functions - - if self.errorok: - # User must have done some kind of panic - # mode recovery on their own. The - # returned token is the next lookahead - lookahead = tok - errtoken = None - continue - else: - if errtoken: - if hasattr(errtoken,"lineno"): lineno = lookahead.lineno - else: lineno = 0 - if lineno: - sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) - else: - sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) - else: - sys.stderr.write("yacc: Parse error in input. EOF\n") - return - - else: - errorcount = error_count - - # case 1: the statestack only has 1 entry on it. If we're in this state, the - # entire parse has been rolled back and we're completely hosed. The token is - # discarded and we just keep going. - - if len(statestack) <= 1 and lookahead.type != '$end': - lookahead = None - errtoken = None - state = 0 - # Nuke the pushback stack - del lookaheadstack[:] - continue - - # case 2: the statestack has a couple of entries on it, but we're - # at the end of the file. nuke the top entry and generate an error token - - # Start nuking entries on the stack - if lookahead.type == '$end': - # Whoa. We're really hosed here. Bail out - return - - if lookahead.type != 'error': - sym = symstack[-1] - if sym.type == 'error': - # Hmmm. Error is on top of stack, we'll just nuke input - # symbol and continue - lookahead = None - continue - t = YaccSymbol() - t.type = 'error' - if hasattr(lookahead,"lineno"): - t.lineno = lookahead.lineno - t.value = lookahead - lookaheadstack.append(lookahead) - lookahead = t - else: - symstack.pop() - statestack.pop() - state = statestack[-1] # Potential bug fix - - continue - - # Call an error function here - raise RuntimeError("yacc: internal parser error!!!\n") - -# ----------------------------------------------------------------------------- -# === Grammar Representation === -# -# The following functions, classes, and variables are used to represent and -# manipulate the rules that make up a grammar. -# ----------------------------------------------------------------------------- - -import re - -# regex matching identifiers -_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') - -# ----------------------------------------------------------------------------- -# class Production: -# -# This class stores the raw information about a single production or grammar rule. -# A grammar rule refers to a specification such as this: -# -# expr : expr PLUS term -# -# Here are the basic attributes defined on all productions -# -# name - Name of the production. For example 'expr' -# prod - A list of symbols on the right side ['expr','PLUS','term'] -# prec - Production precedence level -# number - Production number. -# func - Function that executes on reduce -# file - File where production function is defined -# lineno - Line number where production function is defined -# -# The following attributes are defined or optional. -# -# len - Length of the production (number of symbols on right hand side) -# usyms - Set of unique symbols found in the production -# ----------------------------------------------------------------------------- - -class Production(object): - reduced = 0 - def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0): - self.name = name - self.prod = tuple(prod) - self.number = number - self.func = func - self.callable = None - self.file = file - self.line = line - self.prec = precedence - - # Internal settings used during table construction - - self.len = len(self.prod) # Length of the production - - # Create a list of unique production symbols used in the production - self.usyms = [ ] - for s in self.prod: - if s not in self.usyms: - self.usyms.append(s) - - # List of all LR items for the production - self.lr_items = [] - self.lr_next = None - - # Create a string representation - if self.prod: - self.str = "%s -> %s" % (self.name," ".join(self.prod)) - else: - self.str = "%s -> " % self.name - - def __str__(self): - return self.str - - def __repr__(self): - return "Production("+str(self)+")" - - def __len__(self): - return len(self.prod) - - def __nonzero__(self): - return 1 - - def __getitem__(self,index): - return self.prod[index] - - # Return the nth lr_item from the production (or None if at the end) - def lr_item(self,n): - if n > len(self.prod): return None - p = LRItem(self,n) - - # Precompute the list of productions immediately following. Hack. Remove later - try: - p.lr_after = Prodnames[p.prod[n+1]] - except (IndexError,KeyError): - p.lr_after = [] - try: - p.lr_before = p.prod[n-1] - except IndexError: - p.lr_before = None - - return p - - # Bind the production function name to a callable - def bind(self,pdict): - if self.func: - self.callable = pdict[self.func] - -# This class serves as a minimal standin for Production objects when -# reading table data from files. It only contains information -# actually used by the LR parsing engine, plus some additional -# debugging information. -class MiniProduction(object): - def __init__(self,str,name,len,func,file,line): - self.name = name - self.len = len - self.func = func - self.callable = None - self.file = file - self.line = line - self.str = str - def __str__(self): - return self.str - def __repr__(self): - return "MiniProduction(%s)" % self.str - - # Bind the production function name to a callable - def bind(self,pdict): - if self.func: - self.callable = pdict[self.func] - - -# ----------------------------------------------------------------------------- -# class LRItem -# -# This class represents a specific stage of parsing a production rule. For -# example: -# -# expr : expr . PLUS term -# -# In the above, the "." represents the current location of the parse. Here -# basic attributes: -# -# name - Name of the production. For example 'expr' -# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] -# number - Production number. -# -# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' -# then lr_next refers to 'expr -> expr PLUS . term' -# lr_index - LR item index (location of the ".") in the prod list. -# lookaheads - LALR lookahead symbols for this item -# len - Length of the production (number of symbols on right hand side) -# lr_after - List of all productions that immediately follow -# lr_before - Grammar symbol immediately before -# ----------------------------------------------------------------------------- - -class LRItem(object): - def __init__(self,p,n): - self.name = p.name - self.prod = list(p.prod) - self.number = p.number - self.lr_index = n - self.lookaheads = { } - self.prod.insert(n,".") - self.prod = tuple(self.prod) - self.len = len(self.prod) - self.usyms = p.usyms - - def __str__(self): - if self.prod: - s = "%s -> %s" % (self.name," ".join(self.prod)) - else: - s = "%s -> " % self.name - return s - - def __repr__(self): - return "LRItem("+str(self)+")" - -# ----------------------------------------------------------------------------- -# rightmost_terminal() -# -# Return the rightmost terminal from a list of symbols. Used in add_production() -# ----------------------------------------------------------------------------- -def rightmost_terminal(symbols, terminals): - i = len(symbols) - 1 - while i >= 0: - if symbols[i] in terminals: - return symbols[i] - i -= 1 - return None - -# ----------------------------------------------------------------------------- -# === GRAMMAR CLASS === -# -# The following class represents the contents of the specified grammar along -# with various computed properties such as first sets, follow sets, LR items, etc. -# This data is used for critical parts of the table generation process later. -# ----------------------------------------------------------------------------- - -class GrammarError(YaccError): pass - -class Grammar(object): - def __init__(self,terminals): - self.Productions = [None] # A list of all of the productions. The first - # entry is always reserved for the purpose of - # building an augmented grammar - - self.Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all - # productions of that nonterminal. - - self.Prodmap = { } # A dictionary that is only used to detect duplicate - # productions. - - self.Terminals = { } # A dictionary mapping the names of terminal symbols to a - # list of the rules where they are used. - - for term in terminals: - self.Terminals[term] = [] - - self.Terminals['error'] = [] - - self.Nonterminals = { } # A dictionary mapping names of nonterminals to a list - # of rule numbers where they are used. - - self.First = { } # A dictionary of precomputed FIRST(x) symbols - - self.Follow = { } # A dictionary of precomputed FOLLOW(x) symbols - - self.Precedence = { } # Precedence rules for each terminal. Contains tuples of the - # form ('right',level) or ('nonassoc', level) or ('left',level) - - self.UsedPrecedence = { } # Precedence rules that were actually used by the grammer. - # This is only used to provide error checking and to generate - # a warning about unused precedence rules. - - self.Start = None # Starting symbol for the grammar - - - def __len__(self): - return len(self.Productions) - - def __getitem__(self,index): - return self.Productions[index] - - # ----------------------------------------------------------------------------- - # set_precedence() - # - # Sets the precedence for a given terminal. assoc is the associativity such as - # 'left','right', or 'nonassoc'. level is a numeric level. - # - # ----------------------------------------------------------------------------- - - def set_precedence(self,term,assoc,level): - assert self.Productions == [None],"Must call set_precedence() before add_production()" - if term in self.Precedence: - raise GrammarError("Precedence already specified for terminal '%s'" % term) - if assoc not in ['left','right','nonassoc']: - raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") - self.Precedence[term] = (assoc,level) - - # ----------------------------------------------------------------------------- - # add_production() - # - # Given an action function, this function assembles a production rule and - # computes its precedence level. - # - # The production rule is supplied as a list of symbols. For example, - # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and - # symbols ['expr','PLUS','term']. - # - # Precedence is determined by the precedence of the right-most non-terminal - # or the precedence of a terminal specified by %prec. - # - # A variety of error checks are performed to make sure production symbols - # are valid and that %prec is used correctly. - # ----------------------------------------------------------------------------- - - def add_production(self,prodname,syms,func=None,file='',line=0): - - if prodname in self.Terminals: - raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname)) - if prodname == 'error': - raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname)) - if not _is_identifier.match(prodname): - raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname)) - - # Look for literal tokens - for n,s in enumerate(syms): - if s[0] in "'\"": - try: - c = eval(s) - if (len(c) > 1): - raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname)) - if not c in self.Terminals: - self.Terminals[c] = [] - syms[n] = c - continue - except SyntaxError: - pass - if not _is_identifier.match(s) and s != '%prec': - raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname)) - - # Determine the precedence level - if '%prec' in syms: - if syms[-1] == '%prec': - raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line)) - if syms[-2] != '%prec': - raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line)) - precname = syms[-1] - prodprec = self.Precedence.get(precname,None) - if not prodprec: - raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname)) - else: - self.UsedPrecedence[precname] = 1 - del syms[-2:] # Drop %prec from the rule - else: - # If no %prec, precedence is determined by the rightmost terminal symbol - precname = rightmost_terminal(syms,self.Terminals) - prodprec = self.Precedence.get(precname,('right',0)) - - # See if the rule is already in the rulemap - map = "%s -> %s" % (prodname,syms) - if map in self.Prodmap: - m = self.Prodmap[map] - raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) + - "Previous definition at %s:%d" % (m.file, m.line)) - - # From this point on, everything is valid. Create a new Production instance - pnumber = len(self.Productions) - if not prodname in self.Nonterminals: - self.Nonterminals[prodname] = [ ] - - # Add the production number to Terminals and Nonterminals - for t in syms: - if t in self.Terminals: - self.Terminals[t].append(pnumber) - else: - if not t in self.Nonterminals: - self.Nonterminals[t] = [ ] - self.Nonterminals[t].append(pnumber) - - # Create a production and add it to the list of productions - p = Production(pnumber,prodname,syms,prodprec,func,file,line) - self.Productions.append(p) - self.Prodmap[map] = p - - # Add to the global productions list - try: - self.Prodnames[prodname].append(p) - except KeyError: - self.Prodnames[prodname] = [ p ] - return 0 - - # ----------------------------------------------------------------------------- - # set_start() - # - # Sets the starting symbol and creates the augmented grammar. Production - # rule 0 is S' -> start where start is the start symbol. - # ----------------------------------------------------------------------------- - - def set_start(self,start=None): - if not start: - start = self.Productions[1].name - if start not in self.Nonterminals: - raise GrammarError("start symbol %s undefined" % start) - self.Productions[0] = Production(0,"S'",[start]) - self.Nonterminals[start].append(0) - self.Start = start - - # ----------------------------------------------------------------------------- - # find_unreachable() - # - # Find all of the nonterminal symbols that can't be reached from the starting - # symbol. Returns a list of nonterminals that can't be reached. - # ----------------------------------------------------------------------------- - - def find_unreachable(self): - - # Mark all symbols that are reachable from a symbol s - def mark_reachable_from(s): - if reachable[s]: - # We've already reached symbol s. - return - reachable[s] = 1 - for p in self.Prodnames.get(s,[]): - for r in p.prod: - mark_reachable_from(r) - - reachable = { } - for s in list(self.Terminals) + list(self.Nonterminals): - reachable[s] = 0 - - mark_reachable_from( self.Productions[0].prod[0] ) - - return [s for s in list(self.Nonterminals) - if not reachable[s]] - - # ----------------------------------------------------------------------------- - # infinite_cycles() - # - # This function looks at the various parsing rules and tries to detect - # infinite recursion cycles (grammar rules where there is no possible way - # to derive a string of only terminals). - # ----------------------------------------------------------------------------- - - def infinite_cycles(self): - terminates = {} - - # Terminals: - for t in self.Terminals: - terminates[t] = 1 - - terminates['$end'] = 1 - - # Nonterminals: - - # Initialize to false: - for n in self.Nonterminals: - terminates[n] = 0 - - # Then propagate termination until no change: - while 1: - some_change = 0 - for (n,pl) in self.Prodnames.items(): - # Nonterminal n terminates iff any of its productions terminates. - for p in pl: - # Production p terminates iff all of its rhs symbols terminate. - for s in p.prod: - if not terminates[s]: - # The symbol s does not terminate, - # so production p does not terminate. - p_terminates = 0 - break - else: - # didn't break from the loop, - # so every symbol s terminates - # so production p terminates. - p_terminates = 1 - - if p_terminates: - # symbol n terminates! - if not terminates[n]: - terminates[n] = 1 - some_change = 1 - # Don't need to consider any more productions for this n. - break - - if not some_change: - break - - infinite = [] - for (s,term) in terminates.items(): - if not term: - if not s in self.Prodnames and not s in self.Terminals and s != 'error': - # s is used-but-not-defined, and we've already warned of that, - # so it would be overkill to say that it's also non-terminating. - pass - else: - infinite.append(s) - - return infinite - - - # ----------------------------------------------------------------------------- - # undefined_symbols() - # - # Find all symbols that were used the grammar, but not defined as tokens or - # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol - # and prod is the production where the symbol was used. - # ----------------------------------------------------------------------------- - def undefined_symbols(self): - result = [] - for p in self.Productions: - if not p: continue - - for s in p.prod: - if not s in self.Prodnames and not s in self.Terminals and s != 'error': - result.append((s,p)) - return result - - # ----------------------------------------------------------------------------- - # unused_terminals() - # - # Find all terminals that were defined, but not used by the grammar. Returns - # a list of all symbols. - # ----------------------------------------------------------------------------- - def unused_terminals(self): - unused_tok = [] - for s,v in self.Terminals.items(): - if s != 'error' and not v: - unused_tok.append(s) - - return unused_tok - - # ------------------------------------------------------------------------------ - # unused_rules() - # - # Find all grammar rules that were defined, but not used (maybe not reachable) - # Returns a list of productions. - # ------------------------------------------------------------------------------ - - def unused_rules(self): - unused_prod = [] - for s,v in self.Nonterminals.items(): - if not v: - p = self.Prodnames[s][0] - unused_prod.append(p) - return unused_prod - - # ----------------------------------------------------------------------------- - # unused_precedence() - # - # Returns a list of tuples (term,precedence) corresponding to precedence - # rules that were never used by the grammar. term is the name of the terminal - # on which precedence was applied and precedence is a string such as 'left' or - # 'right' corresponding to the type of precedence. - # ----------------------------------------------------------------------------- - - def unused_precedence(self): - unused = [] - for termname in self.Precedence: - if not (termname in self.Terminals or termname in self.UsedPrecedence): - unused.append((termname,self.Precedence[termname][0])) - - return unused - - # ------------------------------------------------------------------------- - # _first() - # - # Compute the value of FIRST1(beta) where beta is a tuple of symbols. - # - # During execution of compute_first1, the result may be incomplete. - # Afterward (e.g., when called from compute_follow()), it will be complete. - # ------------------------------------------------------------------------- - def _first(self,beta): - - # We are computing First(x1,x2,x3,...,xn) - result = [ ] - for x in beta: - x_produces_empty = 0 - - # Add all the non- symbols of First[x] to the result. - for f in self.First[x]: - if f == '': - x_produces_empty = 1 - else: - if f not in result: result.append(f) - - if x_produces_empty: - # We have to consider the next x in beta, - # i.e. stay in the loop. - pass - else: - # We don't have to consider any further symbols in beta. - break - else: - # There was no 'break' from the loop, - # so x_produces_empty was true for all x in beta, - # so beta produces empty as well. - result.append('') - - return result - - # ------------------------------------------------------------------------- - # compute_first() - # - # Compute the value of FIRST1(X) for all symbols - # ------------------------------------------------------------------------- - def compute_first(self): - if self.First: - return self.First - - # Terminals: - for t in self.Terminals: - self.First[t] = [t] - - self.First['$end'] = ['$end'] - - # Nonterminals: - - # Initialize to the empty set: - for n in self.Nonterminals: - self.First[n] = [] - - # Then propagate symbols until no change: - while 1: - some_change = 0 - for n in self.Nonterminals: - for p in self.Prodnames[n]: - for f in self._first(p.prod): - if f not in self.First[n]: - self.First[n].append( f ) - some_change = 1 - if not some_change: - break - - return self.First - - # --------------------------------------------------------------------- - # compute_follow() - # - # Computes all of the follow sets for every non-terminal symbol. The - # follow set is the set of all symbols that might follow a given - # non-terminal. See the Dragon book, 2nd Ed. p. 189. - # --------------------------------------------------------------------- - def compute_follow(self,start=None): - # If already computed, return the result - if self.Follow: - return self.Follow - - # If first sets not computed yet, do that first. - if not self.First: - self.compute_first() - - # Add '$end' to the follow list of the start symbol - for k in self.Nonterminals: - self.Follow[k] = [ ] - - if not start: - start = self.Productions[1].name - - self.Follow[start] = [ '$end' ] - - while 1: - didadd = 0 - for p in self.Productions[1:]: - # Here is the production set - for i in range(len(p.prod)): - B = p.prod[i] - if B in self.Nonterminals: - # Okay. We got a non-terminal in a production - fst = self._first(p.prod[i+1:]) - hasempty = 0 - for f in fst: - if f != '' and f not in self.Follow[B]: - self.Follow[B].append(f) - didadd = 1 - if f == '': - hasempty = 1 - if hasempty or i == (len(p.prod)-1): - # Add elements of follow(a) to follow(b) - for f in self.Follow[p.name]: - if f not in self.Follow[B]: - self.Follow[B].append(f) - didadd = 1 - if not didadd: break - return self.Follow - - - # ----------------------------------------------------------------------------- - # build_lritems() - # - # This function walks the list of productions and builds a complete set of the - # LR items. The LR items are stored in two ways: First, they are uniquely - # numbered and placed in the list _lritems. Second, a linked list of LR items - # is built for each production. For example: - # - # E -> E PLUS E - # - # Creates the list - # - # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] - # ----------------------------------------------------------------------------- - - def build_lritems(self): - for p in self.Productions: - lastlri = p - i = 0 - lr_items = [] - while 1: - if i > len(p): - lri = None - else: - lri = LRItem(p,i) - # Precompute the list of productions immediately following - try: - lri.lr_after = self.Prodnames[lri.prod[i+1]] - except (IndexError,KeyError): - lri.lr_after = [] - try: - lri.lr_before = lri.prod[i-1] - except IndexError: - lri.lr_before = None - - lastlri.lr_next = lri - if not lri: break - lr_items.append(lri) - lastlri = lri - i += 1 - p.lr_items = lr_items - -# ----------------------------------------------------------------------------- -# == Class LRTable == -# -# This basic class represents a basic table of LR parsing information. -# Methods for generating the tables are not defined here. They are defined -# in the derived class LRGeneratedTable. -# ----------------------------------------------------------------------------- - -class VersionError(YaccError): pass - -class LRTable(object): - def __init__(self): - self.lr_action = None - self.lr_goto = None - self.lr_productions = None - self.lr_method = None - - def read_table(self,module): - if isinstance(module,types.ModuleType): - parsetab = module - else: - if sys.version_info[0] < 3: - exec("import %s as parsetab" % module) - else: - env = { } - exec("import %s as parsetab" % module, env, env) - parsetab = env['parsetab'] - - if parsetab._tabversion != __tabversion__: - raise VersionError("yacc table file version is out of date") - - self.lr_action = parsetab._lr_action - self.lr_goto = parsetab._lr_goto - - self.lr_productions = [] - for p in parsetab._lr_productions: - self.lr_productions.append(MiniProduction(*p)) - - self.lr_method = parsetab._lr_method - return parsetab._lr_signature - - def read_pickle(self,filename): - try: - import cPickle as pickle - except ImportError: - import pickle - - in_f = open(filename,"rb") - - tabversion = pickle.load(in_f) - if tabversion != __tabversion__: - raise VersionError("yacc table file version is out of date") - self.lr_method = pickle.load(in_f) - signature = pickle.load(in_f) - self.lr_action = pickle.load(in_f) - self.lr_goto = pickle.load(in_f) - productions = pickle.load(in_f) - - self.lr_productions = [] - for p in productions: - self.lr_productions.append(MiniProduction(*p)) - - in_f.close() - return signature - - # Bind all production function names to callable objects in pdict - def bind_callables(self,pdict): - for p in self.lr_productions: - p.bind(pdict) - -# ----------------------------------------------------------------------------- -# === LR Generator === -# -# The following classes and functions are used to generate LR parsing tables on -# a grammar. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# digraph() -# traverse() -# -# The following two functions are used to compute set valued functions -# of the form: -# -# F(x) = F'(x) U U{F(y) | x R y} -# -# This is used to compute the values of Read() sets as well as FOLLOW sets -# in LALR(1) generation. -# -# Inputs: X - An input set -# R - A relation -# FP - Set-valued function -# ------------------------------------------------------------------------------ - -def digraph(X,R,FP): - N = { } - for x in X: - N[x] = 0 - stack = [] - F = { } - for x in X: - if N[x] == 0: traverse(x,N,stack,F,X,R,FP) - return F - -def traverse(x,N,stack,F,X,R,FP): - stack.append(x) - d = len(stack) - N[x] = d - F[x] = FP(x) # F(X) <- F'(x) - - rel = R(x) # Get y's related to x - for y in rel: - if N[y] == 0: - traverse(y,N,stack,F,X,R,FP) - N[x] = min(N[x],N[y]) - for a in F.get(y,[]): - if a not in F[x]: F[x].append(a) - if N[x] == d: - N[stack[-1]] = MAXINT - F[stack[-1]] = F[x] - element = stack.pop() - while element != x: - N[stack[-1]] = MAXINT - F[stack[-1]] = F[x] - element = stack.pop() - -class LALRError(YaccError): pass - -# ----------------------------------------------------------------------------- -# == LRGeneratedTable == -# -# This class implements the LR table generation algorithm. There are no -# public methods except for write() -# ----------------------------------------------------------------------------- - -class LRGeneratedTable(LRTable): - def __init__(self,grammar,method='LALR',log=None): - if method not in ['SLR','LALR']: - raise LALRError("Unsupported method %s" % method) - - self.grammar = grammar - self.lr_method = method - - # Set up the logger - if not log: - log = NullLogger() - self.log = log - - # Internal attributes - self.lr_action = {} # Action table - self.lr_goto = {} # Goto table - self.lr_productions = grammar.Productions # Copy of grammar Production array - self.lr_goto_cache = {} # Cache of computed gotos - self.lr0_cidhash = {} # Cache of closures - - self._add_count = 0 # Internal counter used to detect cycles - - # Diagonistic information filled in by the table generator - self.sr_conflict = 0 - self.rr_conflict = 0 - self.conflicts = [] # List of conflicts - - self.sr_conflicts = [] - self.rr_conflicts = [] - - # Build the tables - self.grammar.build_lritems() - self.grammar.compute_first() - self.grammar.compute_follow() - self.lr_parse_table() - - # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. - - def lr0_closure(self,I): - self._add_count += 1 - - # Add everything in I to J - J = I[:] - didadd = 1 - while didadd: - didadd = 0 - for j in J: - for x in j.lr_after: - if getattr(x,"lr0_added",0) == self._add_count: continue - # Add B --> .G to J - J.append(x.lr_next) - x.lr0_added = self._add_count - didadd = 1 - - return J - - # Compute the LR(0) goto function goto(I,X) where I is a set - # of LR(0) items and X is a grammar symbol. This function is written - # in a way that guarantees uniqueness of the generated goto sets - # (i.e. the same goto set will never be returned as two different Python - # objects). With uniqueness, we can later do fast set comparisons using - # id(obj) instead of element-wise comparison. - - def lr0_goto(self,I,x): - # First we look for a previously cached entry - g = self.lr_goto_cache.get((id(I),x),None) - if g: return g - - # Now we generate the goto set in a way that guarantees uniqueness - # of the result - - s = self.lr_goto_cache.get(x,None) - if not s: - s = { } - self.lr_goto_cache[x] = s - - gs = [ ] - for p in I: - n = p.lr_next - if n and n.lr_before == x: - s1 = s.get(id(n),None) - if not s1: - s1 = { } - s[id(n)] = s1 - gs.append(n) - s = s1 - g = s.get('$end',None) - if not g: - if gs: - g = self.lr0_closure(gs) - s['$end'] = g - else: - s['$end'] = gs - self.lr_goto_cache[(id(I),x)] = g - return g - - # Compute the LR(0) sets of item function - def lr0_items(self): - - C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ] - i = 0 - for I in C: - self.lr0_cidhash[id(I)] = i - i += 1 - - # Loop over the items in C and each grammar symbols - i = 0 - while i < len(C): - I = C[i] - i += 1 - - # Collect all of the symbols that could possibly be in the goto(I,X) sets - asyms = { } - for ii in I: - for s in ii.usyms: - asyms[s] = None - - for x in asyms: - g = self.lr0_goto(I,x) - if not g: continue - if id(g) in self.lr0_cidhash: continue - self.lr0_cidhash[id(g)] = len(C) - C.append(g) - - return C - - # ----------------------------------------------------------------------------- - # ==== LALR(1) Parsing ==== - # - # LALR(1) parsing is almost exactly the same as SLR except that instead of - # relying upon Follow() sets when performing reductions, a more selective - # lookahead set that incorporates the state of the LR(0) machine is utilized. - # Thus, we mainly just have to focus on calculating the lookahead sets. - # - # The method used here is due to DeRemer and Pennelo (1982). - # - # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) - # Lookahead Sets", ACM Transactions on Programming Languages and Systems, - # Vol. 4, No. 4, Oct. 1982, pp. 615-649 - # - # Further details can also be found in: - # - # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", - # McGraw-Hill Book Company, (1985). - # - # ----------------------------------------------------------------------------- - - # ----------------------------------------------------------------------------- - # compute_nullable_nonterminals() - # - # Creates a dictionary containing all of the non-terminals that might produce - # an empty production. - # ----------------------------------------------------------------------------- - - def compute_nullable_nonterminals(self): - nullable = {} - num_nullable = 0 - while 1: - for p in self.grammar.Productions[1:]: - if p.len == 0: - nullable[p.name] = 1 - continue - for t in p.prod: - if not t in nullable: break - else: - nullable[p.name] = 1 - if len(nullable) == num_nullable: break - num_nullable = len(nullable) - return nullable - - # ----------------------------------------------------------------------------- - # find_nonterminal_trans(C) - # - # Given a set of LR(0) items, this functions finds all of the non-terminal - # transitions. These are transitions in which a dot appears immediately before - # a non-terminal. Returns a list of tuples of the form (state,N) where state - # is the state number and N is the nonterminal symbol. - # - # The input C is the set of LR(0) items. - # ----------------------------------------------------------------------------- - - def find_nonterminal_transitions(self,C): - trans = [] - for state in range(len(C)): - for p in C[state]: - if p.lr_index < p.len - 1: - t = (state,p.prod[p.lr_index+1]) - if t[1] in self.grammar.Nonterminals: - if t not in trans: trans.append(t) - state = state + 1 - return trans - - # ----------------------------------------------------------------------------- - # dr_relation() - # - # Computes the DR(p,A) relationships for non-terminal transitions. The input - # is a tuple (state,N) where state is a number and N is a nonterminal symbol. - # - # Returns a list of terminals. - # ----------------------------------------------------------------------------- - - def dr_relation(self,C,trans,nullable): - dr_set = { } - state,N = trans - terms = [] - - g = self.lr0_goto(C[state],N) - for p in g: - if p.lr_index < p.len - 1: - a = p.prod[p.lr_index+1] - if a in self.grammar.Terminals: - if a not in terms: terms.append(a) - - # This extra bit is to handle the start state - if state == 0 and N == self.grammar.Productions[0].prod[0]: - terms.append('$end') - - return terms - - # ----------------------------------------------------------------------------- - # reads_relation() - # - # Computes the READS() relation (p,A) READS (t,C). - # ----------------------------------------------------------------------------- - - def reads_relation(self,C, trans, empty): - # Look for empty transitions - rel = [] - state, N = trans - - g = self.lr0_goto(C[state],N) - j = self.lr0_cidhash.get(id(g),-1) - for p in g: - if p.lr_index < p.len - 1: - a = p.prod[p.lr_index + 1] - if a in empty: - rel.append((j,a)) - - return rel - - # ----------------------------------------------------------------------------- - # compute_lookback_includes() - # - # Determines the lookback and includes relations - # - # LOOKBACK: - # - # This relation is determined by running the LR(0) state machine forward. - # For example, starting with a production "N : . A B C", we run it forward - # to obtain "N : A B C ." We then build a relationship between this final - # state and the starting state. These relationships are stored in a dictionary - # lookdict. - # - # INCLUDES: - # - # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). - # - # This relation is used to determine non-terminal transitions that occur - # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) - # if the following holds: - # - # B -> LAT, where T -> epsilon and p' -L-> p - # - # L is essentially a prefix (which may be empty), T is a suffix that must be - # able to derive an empty string. State p' must lead to state p with the string L. - # - # ----------------------------------------------------------------------------- - - def compute_lookback_includes(self,C,trans,nullable): - - lookdict = {} # Dictionary of lookback relations - includedict = {} # Dictionary of include relations - - # Make a dictionary of non-terminal transitions - dtrans = {} - for t in trans: - dtrans[t] = 1 - - # Loop over all transitions and compute lookbacks and includes - for state,N in trans: - lookb = [] - includes = [] - for p in C[state]: - if p.name != N: continue - - # Okay, we have a name match. We now follow the production all the way - # through the state machine until we get the . on the right hand side - - lr_index = p.lr_index - j = state - while lr_index < p.len - 1: - lr_index = lr_index + 1 - t = p.prod[lr_index] - - # Check to see if this symbol and state are a non-terminal transition - if (j,t) in dtrans: - # Yes. Okay, there is some chance that this is an includes relation - # the only way to know for certain is whether the rest of the - # production derives empty - - li = lr_index + 1 - while li < p.len: - if p.prod[li] in self.grammar.Terminals: break # No forget it - if not p.prod[li] in nullable: break - li = li + 1 - else: - # Appears to be a relation between (j,t) and (state,N) - includes.append((j,t)) - - g = self.lr0_goto(C[j],t) # Go to next set - j = self.lr0_cidhash.get(id(g),-1) # Go to next state - - # When we get here, j is the final state, now we have to locate the production - for r in C[j]: - if r.name != p.name: continue - if r.len != p.len: continue - i = 0 - # This look is comparing a production ". A B C" with "A B C ." - while i < r.lr_index: - if r.prod[i] != p.prod[i+1]: break - i = i + 1 - else: - lookb.append((j,r)) - for i in includes: - if not i in includedict: includedict[i] = [] - includedict[i].append((state,N)) - lookdict[(state,N)] = lookb - - return lookdict,includedict - - # ----------------------------------------------------------------------------- - # compute_read_sets() - # - # Given a set of LR(0) items, this function computes the read sets. - # - # Inputs: C = Set of LR(0) items - # ntrans = Set of nonterminal transitions - # nullable = Set of empty transitions - # - # Returns a set containing the read sets - # ----------------------------------------------------------------------------- - - def compute_read_sets(self,C, ntrans, nullable): - FP = lambda x: self.dr_relation(C,x,nullable) - R = lambda x: self.reads_relation(C,x,nullable) - F = digraph(ntrans,R,FP) - return F - - # ----------------------------------------------------------------------------- - # compute_follow_sets() - # - # Given a set of LR(0) items, a set of non-terminal transitions, a readset, - # and an include set, this function computes the follow sets - # - # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} - # - # Inputs: - # ntrans = Set of nonterminal transitions - # readsets = Readset (previously computed) - # inclsets = Include sets (previously computed) - # - # Returns a set containing the follow sets - # ----------------------------------------------------------------------------- - - def compute_follow_sets(self,ntrans,readsets,inclsets): - FP = lambda x: readsets[x] - R = lambda x: inclsets.get(x,[]) - F = digraph(ntrans,R,FP) - return F - - # ----------------------------------------------------------------------------- - # add_lookaheads() - # - # Attaches the lookahead symbols to grammar rules. - # - # Inputs: lookbacks - Set of lookback relations - # followset - Computed follow set - # - # This function directly attaches the lookaheads to productions contained - # in the lookbacks set - # ----------------------------------------------------------------------------- - - def add_lookaheads(self,lookbacks,followset): - for trans,lb in lookbacks.items(): - # Loop over productions in lookback - for state,p in lb: - if not state in p.lookaheads: - p.lookaheads[state] = [] - f = followset.get(trans,[]) - for a in f: - if a not in p.lookaheads[state]: p.lookaheads[state].append(a) - - # ----------------------------------------------------------------------------- - # add_lalr_lookaheads() - # - # This function does all of the work of adding lookahead information for use - # with LALR parsing - # ----------------------------------------------------------------------------- - - def add_lalr_lookaheads(self,C): - # Determine all of the nullable nonterminals - nullable = self.compute_nullable_nonterminals() - - # Find all non-terminal transitions - trans = self.find_nonterminal_transitions(C) - - # Compute read sets - readsets = self.compute_read_sets(C,trans,nullable) - - # Compute lookback/includes relations - lookd, included = self.compute_lookback_includes(C,trans,nullable) - - # Compute LALR FOLLOW sets - followsets = self.compute_follow_sets(trans,readsets,included) - - # Add all of the lookaheads - self.add_lookaheads(lookd,followsets) - - # ----------------------------------------------------------------------------- - # lr_parse_table() - # - # This function constructs the parse tables for SLR or LALR - # ----------------------------------------------------------------------------- - def lr_parse_table(self): - Productions = self.grammar.Productions - Precedence = self.grammar.Precedence - goto = self.lr_goto # Goto array - action = self.lr_action # Action array - log = self.log # Logger for output - - actionp = { } # Action production array (temporary) - - log.info("Parsing method: %s", self.lr_method) - - # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items - # This determines the number of states - - C = self.lr0_items() - - if self.lr_method == 'LALR': - self.add_lalr_lookaheads(C) - - # Build the parser table, state by state - st = 0 - for I in C: - # Loop over each production in I - actlist = [ ] # List of actions - st_action = { } - st_actionp = { } - st_goto = { } - log.info("") - log.info("state %d", st) - log.info("") - for p in I: - log.info(" (%d) %s", p.number, str(p)) - log.info("") - - for p in I: - if p.len == p.lr_index + 1: - if p.name == "S'": - # Start symbol. Accept! - st_action["$end"] = 0 - st_actionp["$end"] = p - else: - # We are at the end of a production. Reduce! - if self.lr_method == 'LALR': - laheads = p.lookaheads[st] - else: - laheads = self.grammar.Follow[p.name] - for a in laheads: - actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p))) - r = st_action.get(a,None) - if r is not None: - # Whoa. Have a shift/reduce or reduce/reduce conflict - if r > 0: - # Need to decide on shift or reduce here - # By default we favor shifting. Need to add - # some precedence rules here. - sprec,slevel = Productions[st_actionp[a].number].prec - rprec,rlevel = Precedence.get(a,('right',0)) - if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): - # We really need to reduce here. - st_action[a] = -p.number - st_actionp[a] = p - if not slevel and not rlevel: - log.info(" ! shift/reduce conflict for %s resolved as reduce",a) - self.sr_conflicts.append((st,a,'reduce')) - Productions[p.number].reduced += 1 - elif (slevel == rlevel) and (rprec == 'nonassoc'): - st_action[a] = None - else: - # Hmmm. Guess we'll keep the shift - if not rlevel: - log.info(" ! shift/reduce conflict for %s resolved as shift",a) - self.sr_conflicts.append((st,a,'shift')) - elif r < 0: - # Reduce/reduce conflict. In this case, we favor the rule - # that was defined first in the grammar file - oldp = Productions[-r] - pp = Productions[p.number] - if oldp.line > pp.line: - st_action[a] = -p.number - st_actionp[a] = p - chosenp,rejectp = pp,oldp - Productions[p.number].reduced += 1 - Productions[oldp.number].reduced -= 1 - else: - chosenp,rejectp = oldp,pp - self.rr_conflicts.append((st,chosenp,rejectp)) - log.info(" ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a]) - else: - raise LALRError("Unknown conflict in state %d" % st) - else: - st_action[a] = -p.number - st_actionp[a] = p - Productions[p.number].reduced += 1 - else: - i = p.lr_index - a = p.prod[i+1] # Get symbol right after the "." - if a in self.grammar.Terminals: - g = self.lr0_goto(I,a) - j = self.lr0_cidhash.get(id(g),-1) - if j >= 0: - # We are in a shift state - actlist.append((a,p,"shift and go to state %d" % j)) - r = st_action.get(a,None) - if r is not None: - # Whoa have a shift/reduce or shift/shift conflict - if r > 0: - if r != j: - raise LALRError("Shift/shift conflict in state %d" % st) - elif r < 0: - # Do a precedence check. - # - if precedence of reduce rule is higher, we reduce. - # - if precedence of reduce is same and left assoc, we reduce. - # - otherwise we shift - rprec,rlevel = Productions[st_actionp[a].number].prec - sprec,slevel = Precedence.get(a,('right',0)) - if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): - # We decide to shift here... highest precedence to shift - Productions[st_actionp[a].number].reduced -= 1 - st_action[a] = j - st_actionp[a] = p - if not rlevel: - log.info(" ! shift/reduce conflict for %s resolved as shift",a) - self.sr_conflicts.append((st,a,'shift')) - elif (slevel == rlevel) and (rprec == 'nonassoc'): - st_action[a] = None - else: - # Hmmm. Guess we'll keep the reduce - if not slevel and not rlevel: - log.info(" ! shift/reduce conflict for %s resolved as reduce",a) - self.sr_conflicts.append((st,a,'reduce')) - - else: - raise LALRError("Unknown conflict in state %d" % st) - else: - st_action[a] = j - st_actionp[a] = p - - # Print the actions associated with each terminal - _actprint = { } - for a,p,m in actlist: - if a in st_action: - if p is st_actionp[a]: - log.info(" %-15s %s",a,m) - _actprint[(a,m)] = 1 - log.info("") - # Print the actions that were not used. (debugging) - not_used = 0 - for a,p,m in actlist: - if a in st_action: - if p is not st_actionp[a]: - if not (a,m) in _actprint: - log.debug(" ! %-15s [ %s ]",a,m) - not_used = 1 - _actprint[(a,m)] = 1 - if not_used: - log.debug("") - - # Construct the goto table for this state - - nkeys = { } - for ii in I: - for s in ii.usyms: - if s in self.grammar.Nonterminals: - nkeys[s] = None - for n in nkeys: - g = self.lr0_goto(I,n) - j = self.lr0_cidhash.get(id(g),-1) - if j >= 0: - st_goto[n] = j - log.info(" %-30s shift and go to state %d",n,j) - - action[st] = st_action - actionp[st] = st_actionp - goto[st] = st_goto - st += 1 - - - # ----------------------------------------------------------------------------- - # write() - # - # This function writes the LR parsing tables to a file - # ----------------------------------------------------------------------------- - - def write_table(self,modulename,outputdir='',signature=""): - basemodulename = modulename.split(".")[-1] - filename = os.path.join(outputdir,basemodulename) + ".py" - try: - f = open(filename,"w") - - f.write(""" -# %s -# This file is automatically generated. Do not edit. -_tabversion = %r - -_lr_method = %r - -_lr_signature = %r - """ % (filename, __tabversion__, self.lr_method, signature)) - - # Change smaller to 0 to go back to original tables - smaller = 1 - - # Factor out names to try and make smaller - if smaller: - items = { } - - for s,nd in self.lr_action.items(): - for name,v in nd.items(): - i = items.get(name) - if not i: - i = ([],[]) - items[name] = i - i[0].append(s) - i[1].append(v) - - f.write("\n_lr_action_items = {") - for k,v in items.items(): - f.write("%r:([" % k) - for i in v[0]: - f.write("%r," % i) - f.write("],[") - for i in v[1]: - f.write("%r," % i) - - f.write("]),") - f.write("}\n") - - f.write(""" -_lr_action = { } -for _k, _v in _lr_action_items.items(): - for _x,_y in zip(_v[0],_v[1]): - if not _x in _lr_action: _lr_action[_x] = { } - _lr_action[_x][_k] = _y -del _lr_action_items -""") - - else: - f.write("\n_lr_action = { "); - for k,v in self.lr_action.items(): - f.write("(%r,%r):%r," % (k[0],k[1],v)) - f.write("}\n"); - - if smaller: - # Factor out names to try and make smaller - items = { } - - for s,nd in self.lr_goto.items(): - for name,v in nd.items(): - i = items.get(name) - if not i: - i = ([],[]) - items[name] = i - i[0].append(s) - i[1].append(v) - - f.write("\n_lr_goto_items = {") - for k,v in items.items(): - f.write("%r:([" % k) - for i in v[0]: - f.write("%r," % i) - f.write("],[") - for i in v[1]: - f.write("%r," % i) - - f.write("]),") - f.write("}\n") - - f.write(""" -_lr_goto = { } -for _k, _v in _lr_goto_items.items(): - for _x,_y in zip(_v[0],_v[1]): - if not _x in _lr_goto: _lr_goto[_x] = { } - _lr_goto[_x][_k] = _y -del _lr_goto_items -""") - else: - f.write("\n_lr_goto = { "); - for k,v in self.lr_goto.items(): - f.write("(%r,%r):%r," % (k[0],k[1],v)) - f.write("}\n"); - - # Write production table - f.write("_lr_productions = [\n") - for p in self.lr_productions: - if p.func: - f.write(" (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line)) - else: - f.write(" (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len)) - f.write("]\n") - f.close() - - except IOError: - e = sys.exc_info()[1] - sys.stderr.write("Unable to create '%s'\n" % filename) - sys.stderr.write(str(e)+"\n") - return - - - # ----------------------------------------------------------------------------- - # pickle_table() - # - # This function pickles the LR parsing tables to a supplied file object - # ----------------------------------------------------------------------------- - - def pickle_table(self,filename,signature=""): - try: - import cPickle as pickle - except ImportError: - import pickle - outf = open(filename,"wb") - pickle.dump(__tabversion__,outf,pickle_protocol) - pickle.dump(self.lr_method,outf,pickle_protocol) - pickle.dump(signature,outf,pickle_protocol) - pickle.dump(self.lr_action,outf,pickle_protocol) - pickle.dump(self.lr_goto,outf,pickle_protocol) - - outp = [] - for p in self.lr_productions: - if p.func: - outp.append((p.str,p.name, p.len, p.func,p.file,p.line)) - else: - outp.append((str(p),p.name,p.len,None,None,None)) - pickle.dump(outp,outf,pickle_protocol) - outf.close() - -# ----------------------------------------------------------------------------- -# === INTROSPECTION === -# -# The following functions and classes are used to implement the PLY -# introspection features followed by the yacc() function itself. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# get_caller_module_dict() -# -# This function returns a dictionary containing all of the symbols defined within -# a caller further down the call stack. This is used to get the environment -# associated with the yacc() call if none was provided. -# ----------------------------------------------------------------------------- - -def get_caller_module_dict(levels): - try: - raise RuntimeError - except RuntimeError: - e,b,t = sys.exc_info() - f = t.tb_frame - while levels > 0: - f = f.f_back - levels -= 1 - ldict = f.f_globals.copy() - if f.f_globals != f.f_locals: - ldict.update(f.f_locals) - - return ldict - -# ----------------------------------------------------------------------------- -# parse_grammar() -# -# This takes a raw grammar rule string and parses it into production data -# ----------------------------------------------------------------------------- -def parse_grammar(doc,file,line): - grammar = [] - # Split the doc string into lines - pstrings = doc.splitlines() - lastp = None - dline = line - for ps in pstrings: - dline += 1 - p = ps.split() - if not p: continue - try: - if p[0] == '|': - # This is a continuation of a previous rule - if not lastp: - raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline)) - prodname = lastp - syms = p[1:] - else: - prodname = p[0] - lastp = prodname - syms = p[2:] - assign = p[1] - if assign != ':' and assign != '::=': - raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline)) - - grammar.append((file,dline,prodname,syms)) - except SyntaxError: - raise - except Exception: - raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip())) - - return grammar - -# ----------------------------------------------------------------------------- -# ParserReflect() -# -# This class represents information extracted for building a parser including -# start symbol, error function, tokens, precedence list, action functions, -# etc. -# ----------------------------------------------------------------------------- -class ParserReflect(object): - def __init__(self,pdict,log=None): - self.pdict = pdict - self.start = None - self.error_func = None - self.tokens = None - self.files = {} - self.grammar = [] - self.error = 0 - - if log is None: - self.log = PlyLogger(sys.stderr) - else: - self.log = log - - # Get all of the basic information - def get_all(self): - self.get_start() - self.get_error_func() - self.get_tokens() - self.get_precedence() - self.get_pfunctions() - - # Validate all of the information - def validate_all(self): - self.validate_start() - self.validate_error_func() - self.validate_tokens() - self.validate_precedence() - self.validate_pfunctions() - self.validate_files() - return self.error - - # Compute a signature over the grammar - def signature(self): - try: - from hashlib import md5 - except ImportError: - from md5 import md5 - try: - sig = md5() - if self.start: - sig.update(self.start.encode('latin-1')) - if self.prec: - sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1')) - if self.tokens: - sig.update(" ".join(self.tokens).encode('latin-1')) - for f in self.pfuncs: - if f[3]: - sig.update(f[3].encode('latin-1')) - except (TypeError,ValueError): - pass - return sig.digest() - - # ----------------------------------------------------------------------------- - # validate_file() - # - # This method checks to see if there are duplicated p_rulename() functions - # in the parser module file. Without this function, it is really easy for - # users to make mistakes by cutting and pasting code fragments (and it's a real - # bugger to try and figure out why the resulting parser doesn't work). Therefore, - # we just do a little regular expression pattern matching of def statements - # to try and detect duplicates. - # ----------------------------------------------------------------------------- - - def validate_files(self): - # Match def p_funcname( - fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') - - for filename in self.files.keys(): - base,ext = os.path.splitext(filename) - if ext != '.py': return 1 # No idea. Assume it's okay. - - try: - f = open(filename) - lines = f.readlines() - f.close() - except IOError: - continue - - counthash = { } - for linen,l in enumerate(lines): - linen += 1 - m = fre.match(l) - if m: - name = m.group(1) - prev = counthash.get(name) - if not prev: - counthash[name] = linen - else: - self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev) - - # Get the start symbol - def get_start(self): - self.start = self.pdict.get('start') - - # Validate the start symbol - def validate_start(self): - if self.start is not None: - if not isinstance(self.start,str): - self.log.error("'start' must be a string") - - # Look for error handler - def get_error_func(self): - self.error_func = self.pdict.get('p_error') - - # Validate the error function - def validate_error_func(self): - if self.error_func: - if isinstance(self.error_func,types.FunctionType): - ismethod = 0 - elif isinstance(self.error_func, types.MethodType): - ismethod = 1 - else: - self.log.error("'p_error' defined, but is not a function or method") - self.error = 1 - return - - eline = func_code(self.error_func).co_firstlineno - efile = func_code(self.error_func).co_filename - self.files[efile] = 1 - - if (func_code(self.error_func).co_argcount != 1+ismethod): - self.log.error("%s:%d: p_error() requires 1 argument",efile,eline) - self.error = 1 - - # Get the tokens map - def get_tokens(self): - tokens = self.pdict.get("tokens",None) - if not tokens: - self.log.error("No token list is defined") - self.error = 1 - return - - if not isinstance(tokens,(list, tuple)): - self.log.error("tokens must be a list or tuple") - self.error = 1 - return - - if not tokens: - self.log.error("tokens is empty") - self.error = 1 - return - - self.tokens = tokens - - # Validate the tokens - def validate_tokens(self): - # Validate the tokens. - if 'error' in self.tokens: - self.log.error("Illegal token name 'error'. Is a reserved word") - self.error = 1 - return - - terminals = {} - for n in self.tokens: - if n in terminals: - self.log.warning("Token '%s' multiply defined", n) - terminals[n] = 1 - - # Get the precedence map (if any) - def get_precedence(self): - self.prec = self.pdict.get("precedence",None) - - # Validate and parse the precedence map - def validate_precedence(self): - preclist = [] - if self.prec: - if not isinstance(self.prec,(list,tuple)): - self.log.error("precedence must be a list or tuple") - self.error = 1 - return - for level,p in enumerate(self.prec): - if not isinstance(p,(list,tuple)): - self.log.error("Bad precedence table") - self.error = 1 - return - - if len(p) < 2: - self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p) - self.error = 1 - return - assoc = p[0] - if not isinstance(assoc,str): - self.log.error("precedence associativity must be a string") - self.error = 1 - return - for term in p[1:]: - if not isinstance(term,str): - self.log.error("precedence items must be strings") - self.error = 1 - return - preclist.append((term,assoc,level+1)) - self.preclist = preclist - - # Get all p_functions from the grammar - def get_pfunctions(self): - p_functions = [] - for name, item in self.pdict.items(): - if name[:2] != 'p_': continue - if name == 'p_error': continue - if isinstance(item,(types.FunctionType,types.MethodType)): - line = func_code(item).co_firstlineno - file = func_code(item).co_filename - p_functions.append((line,file,name,item.__doc__)) - - # Sort all of the actions by line number - p_functions.sort() - self.pfuncs = p_functions - - - # Validate all of the p_functions - def validate_pfunctions(self): - grammar = [] - # Check for non-empty symbols - if len(self.pfuncs) == 0: - self.log.error("no rules of the form p_rulename are defined") - self.error = 1 - return - - for line, file, name, doc in self.pfuncs: - func = self.pdict[name] - if isinstance(func, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - if func_code(func).co_argcount > reqargs: - self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__) - self.error = 1 - elif func_code(func).co_argcount < reqargs: - self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__) - self.error = 1 - elif not func.__doc__: - self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__) - else: - try: - parsed_g = parse_grammar(doc,file,line) - for g in parsed_g: - grammar.append((name, g)) - except SyntaxError: - e = sys.exc_info()[1] - self.log.error(str(e)) - self.error = 1 - - # Looks like a valid grammar rule - # Mark the file in which defined. - self.files[file] = 1 - - # Secondary validation step that looks for p_ definitions that are not functions - # or functions that look like they might be grammar rules. - - for n,v in self.pdict.items(): - if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue - if n[0:2] == 't_': continue - if n[0:2] == 'p_' and n != 'p_error': - self.log.warning("'%s' not defined as a function", n) - if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or - (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)): - try: - doc = v.__doc__.split(" ") - if doc[1] == ':': - self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix", - func_code(v).co_filename, func_code(v).co_firstlineno,n) - except Exception: - pass - - self.grammar = grammar - -# ----------------------------------------------------------------------------- -# yacc(module) -# -# Build a parser -# ----------------------------------------------------------------------------- - -def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, - check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='', - debuglog=None, errorlog = None, picklefile=None): - - global parse # Reference to the parsing method of the last built parser - - # If pickling is enabled, table files are not created - - if picklefile: - write_tables = 0 - - if errorlog is None: - errorlog = PlyLogger(sys.stderr) - - # Get the module dictionary used for the parser - if module: - _items = [(k,getattr(module,k)) for k in dir(module)] - pdict = dict(_items) - else: - pdict = get_caller_module_dict(2) - - # Collect parser information from the dictionary - pinfo = ParserReflect(pdict,log=errorlog) - pinfo.get_all() - - if pinfo.error: - raise YaccError("Unable to build parser") - - # Check signature against table files (if any) - signature = pinfo.signature() - - # Read the tables - try: - lr = LRTable() - if picklefile: - read_signature = lr.read_pickle(picklefile) - else: - read_signature = lr.read_table(tabmodule) - if optimize or (read_signature == signature): - try: - lr.bind_callables(pinfo.pdict) - parser = LRParser(lr,pinfo.error_func) - parse = parser.parse - return parser - except Exception: - e = sys.exc_info()[1] - errorlog.warning("There was a problem loading the table file: %s", repr(e)) - except VersionError: - e = sys.exc_info() - errorlog.warning(str(e)) - except Exception: - pass - - if debuglog is None: - if debug: - debuglog = PlyLogger(open(debugfile,"w")) - else: - debuglog = NullLogger() - - debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__) - - - errors = 0 - - # Validate the parser information - if pinfo.validate_all(): - raise YaccError("Unable to build parser") - - if not pinfo.error_func: - errorlog.warning("no p_error() function is defined") - - # Create a grammar object - grammar = Grammar(pinfo.tokens) - - # Set precedence level for terminals - for term, assoc, level in pinfo.preclist: - try: - grammar.set_precedence(term,assoc,level) - except GrammarError: - e = sys.exc_info()[1] - errorlog.warning("%s",str(e)) - - # Add productions to the grammar - for funcname, gram in pinfo.grammar: - file, line, prodname, syms = gram - try: - grammar.add_production(prodname,syms,funcname,file,line) - except GrammarError: - e = sys.exc_info()[1] - errorlog.error("%s",str(e)) - errors = 1 - - # Set the grammar start symbols - try: - if start is None: - grammar.set_start(pinfo.start) - else: - grammar.set_start(start) - except GrammarError: - e = sys.exc_info()[1] - errorlog.error(str(e)) - errors = 1 - - if errors: - raise YaccError("Unable to build parser") - - # Verify the grammar structure - undefined_symbols = grammar.undefined_symbols() - for sym, prod in undefined_symbols: - errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym) - errors = 1 - - unused_terminals = grammar.unused_terminals() - if unused_terminals: - debuglog.info("") - debuglog.info("Unused terminals:") - debuglog.info("") - for term in unused_terminals: - errorlog.warning("Token '%s' defined, but not used", term) - debuglog.info(" %s", term) - - # Print out all productions to the debug log - if debug: - debuglog.info("") - debuglog.info("Grammar") - debuglog.info("") - for n,p in enumerate(grammar.Productions): - debuglog.info("Rule %-5d %s", n, p) - - # Find unused non-terminals - unused_rules = grammar.unused_rules() - for prod in unused_rules: - errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name) - - if len(unused_terminals) == 1: - errorlog.warning("There is 1 unused token") - if len(unused_terminals) > 1: - errorlog.warning("There are %d unused tokens", len(unused_terminals)) - - if len(unused_rules) == 1: - errorlog.warning("There is 1 unused rule") - if len(unused_rules) > 1: - errorlog.warning("There are %d unused rules", len(unused_rules)) - - if debug: - debuglog.info("") - debuglog.info("Terminals, with rules where they appear") - debuglog.info("") - terms = list(grammar.Terminals) - terms.sort() - for term in terms: - debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]])) - - debuglog.info("") - debuglog.info("Nonterminals, with rules where they appear") - debuglog.info("") - nonterms = list(grammar.Nonterminals) - nonterms.sort() - for nonterm in nonterms: - debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]])) - debuglog.info("") - - if check_recursion: - unreachable = grammar.find_unreachable() - for u in unreachable: - errorlog.warning("Symbol '%s' is unreachable",u) - - infinite = grammar.infinite_cycles() - for inf in infinite: - errorlog.error("Infinite recursion detected for symbol '%s'", inf) - errors = 1 - - unused_prec = grammar.unused_precedence() - for term, assoc in unused_prec: - errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term) - errors = 1 - - if errors: - raise YaccError("Unable to build parser") - - # Run the LRGeneratedTable on the grammar - if debug: - errorlog.debug("Generating %s tables", method) - - lr = LRGeneratedTable(grammar,method,debuglog) - - if debug: - num_sr = len(lr.sr_conflicts) - - # Report shift/reduce and reduce/reduce conflicts - if num_sr == 1: - errorlog.warning("1 shift/reduce conflict") - elif num_sr > 1: - errorlog.warning("%d shift/reduce conflicts", num_sr) - - num_rr = len(lr.rr_conflicts) - if num_rr == 1: - errorlog.warning("1 reduce/reduce conflict") - elif num_rr > 1: - errorlog.warning("%d reduce/reduce conflicts", num_rr) - - # Write out conflicts to the output file - if debug and (lr.sr_conflicts or lr.rr_conflicts): - debuglog.warning("") - debuglog.warning("Conflicts:") - debuglog.warning("") - - for state, tok, resolution in lr.sr_conflicts: - debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s", tok, state, resolution) - - already_reported = {} - for state, rule, rejected in lr.rr_conflicts: - if (state,id(rule),id(rejected)) in already_reported: - continue - debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) - debuglog.warning("rejected rule (%s) in state %d", rejected,state) - errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) - errorlog.warning("rejected rule (%s) in state %d", rejected, state) - already_reported[state,id(rule),id(rejected)] = 1 - - warned_never = [] - for state, rule, rejected in lr.rr_conflicts: - if not rejected.reduced and (rejected not in warned_never): - debuglog.warning("Rule (%s) is never reduced", rejected) - errorlog.warning("Rule (%s) is never reduced", rejected) - warned_never.append(rejected) - - # Write the table file if requested - if write_tables: - lr.write_table(tabmodule,outputdir,signature) - - # Write a pickled version of the tables - if picklefile: - lr.pickle_table(picklefile,signature) - - # Build the parser - lr.bind_callables(pinfo.pdict) - parser = LRParser(lr,pinfo.error_func) - - parse = parser.parse - return parser diff --git a/prepro/__init__.py b/prepro/__init__.py index 7fa7c7174..66d918c58 100644 --- a/prepro/__init__.py +++ b/prepro/__init__.py @@ -2,8 +2,9 @@ # -*- coding: utf-8 -*- # vim: ts=4:sw=4:et: -from exceptions import PreprocError -from id_ import ID -from definestable import DefinesTable -from macrocall import MacroCall -from args import ArgList, Arg +from .exceptions import PreprocError +from .id_ import ID +from .definestable import DefinesTable +from .macrocall import MacroCall +from .args import Arg +from .args import ArgList diff --git a/prepro/args.py b/prepro/args.py index 498de2e49..2c7b1afde 100644 --- a/prepro/args.py +++ b/prepro/args.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # vim:ts=4:et:sw=4: -from macrocall import MacroCall +from .macrocall import MacroCall class Arg(object): @@ -14,15 +14,12 @@ def __init__(self, value = None, table = None): if value is not None: self.value += [value] - def __len__(self): return len(self.value) - def __str__(self): return self() - def __call__(self, symbolTable = None): result = '' if symbolTable is None: @@ -36,18 +33,15 @@ def __call__(self, symbolTable = None): return result - def addToken(self, token): self.value += [token] - def __iter__(self): if self.value is not None: for x in self.value: yield x - class ArgList(object): ''' Implements an arglist ''' @@ -55,34 +49,28 @@ def __init__(self, table): self.table = table self.value = [] - def __len__(self): return len(self.value) - def __call__(self): if self.value is None: return None return [x() for x in self.value] - def addNewArg(self, value): if value is not None: self.value += [Arg(value, self.table)] - def __iter__(self): for x in self.value: yield x - def __str__(self): if self() is None: return '' result = '(' + ','.join(self()) + ')' - return result def __getitem__(self, key): @@ -90,5 +78,3 @@ def __getitem__(self, key): return None return self.value[key] - - diff --git a/prepro/definestable.py b/prepro/definestable.py index 536c333aa..dfb0c0cf8 100644 --- a/prepro/definestable.py +++ b/prepro/definestable.py @@ -5,13 +5,14 @@ ''' Class for a Table of Defines. Each identifier has a dictionary entry. ''' + import sys import re -from id_ import ID -from exceptions import PreprocError -from output import warning -from output import CURRENT_FILE +from .id_ import ID +from .exceptions import PreprocError +from .output import warning +from .output import CURRENT_FILE RE_ID = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*') diff --git a/prepro/id_.py b/prepro/id_.py index a4b182c2c..bd6ee8f1a 100644 --- a/prepro/id_.py +++ b/prepro/id_.py @@ -2,14 +2,16 @@ # -*- coding: utf-8 -*- # vim: ts=4:sw=4:et: -''' 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 + import copy -from macrocall import MacroCall +from .macrocall import MacroCall from api.debug import __DEBUG__ -from output import CURRENT_FILE +from .output import CURRENT_FILE class ID(object): @@ -41,13 +43,13 @@ def __str__(self): def __dumptable(self, table): ''' Dumps table on screen - for debuggin purposes + for debugging purposes ''' for x in table.table.keys(): - print x, '\t<---', table[x], type(table[x]), + sys.stdout.write("{0}\t<--- {1} {2}".format(x, table[x], type(table[x]))) if isinstance(table[x], ID): - print table[x].value, - print + sys.stdout(" {0}".format(table[x].value)), + sys.stdout.write("\n") def __call__(self, table): __DEBUG__("evaluating id '%s'" % self.name) diff --git a/prepro/macrocall.py b/prepro/macrocall.py index db6a12182..3c34a0237 100644 --- a/prepro/macrocall.py +++ b/prepro/macrocall.py @@ -3,7 +3,7 @@ # vim:ts=4:et:sw=4: import copy -from exceptions import PreprocError +from .exceptions import PreprocError from api.debug import __DEBUG__ diff --git a/setup.py b/setup.py index eed3da6e7..0743346bc 100644 --- a/setup.py +++ b/setup.py @@ -61,5 +61,6 @@ def get_files(folder): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', ], + requires=['six', 'ply'], tags=['BASIC', 'zxspectrum', 'compiler', 'z80'] ) diff --git a/symbols/__init__.py b/symbols/__init__.py index 2ba383429..575fa3f6d 100644 --- a/symbols/__init__.py +++ b/symbols/__init__.py @@ -10,35 +10,35 @@ # ---------------------------------------------------------------------- # ---- AST Symbols ---- -from arglist import SymbolARGLIST as ARGLIST -from argument import SymbolARGUMENT as ARGUMENT -from arrayaccess import SymbolARRAYACCESS as ARRAYACCESS -from arraydecl import SymbolARRAYDECL as ARRAYDECL -from arrayload import SymbolARRAYLOAD as ARRAYLOAD -from asm import SymbolASM as ASM -from binary import SymbolBINARY as BINARY -from block import SymbolBLOCK as BLOCK -from bound import SymbolBOUND as BOUND -from boundlist import SymbolBOUNDLIST as BOUNDLIST -from builtin import SymbolBUILTIN as BUILTIN -from call import SymbolCALL as CALL -from const import SymbolCONST as CONST -from funccall import SymbolFUNCCALL as FUNCCALL -from funcdecl import SymbolFUNCDECL as FUNCDECL -from function import SymbolFUNCTION as FUNCTION -from nop import SymbolNOP as NOP -from number import SymbolNUMBER as NUMBER -from paramdecl import SymbolPARAMDECL as PARAMDECL -from paramlist import SymbolPARAMLIST as PARAMLIST -from sentence import SymbolSENTENCE as SENTENCE -from string_ import SymbolSTRING as STRING -from strslice import SymbolSTRSLICE as STRSLICE -from type_ import SymbolBASICTYPE as BASICTYPE -from type_ import SymbolTYPE as TYPE -from type_ import SymbolTYPEREF as TYPEREF -from typecast import SymbolTYPECAST as TYPECAST -from unary import SymbolUNARY as UNARY -from var import SymbolVAR as VAR -from vararray import SymbolVARARRAY as VARARRAY -from vardecl import SymbolVARDECL as VARDECL -from label import SymbolLABEL as LABEL +from .arglist import SymbolARGLIST as ARGLIST +from .argument import SymbolARGUMENT as ARGUMENT +from .arrayaccess import SymbolARRAYACCESS as ARRAYACCESS +from .arraydecl import SymbolARRAYDECL as ARRAYDECL +from .arrayload import SymbolARRAYLOAD as ARRAYLOAD +from .asm import SymbolASM as ASM +from .binary import SymbolBINARY as BINARY +from .block import SymbolBLOCK as BLOCK +from .bound import SymbolBOUND as BOUND +from .boundlist import SymbolBOUNDLIST as BOUNDLIST +from .builtin import SymbolBUILTIN as BUILTIN +from .call import SymbolCALL as CALL +from .const import SymbolCONST as CONST +from .funccall import SymbolFUNCCALL as FUNCCALL +from .funcdecl import SymbolFUNCDECL as FUNCDECL +from .function import SymbolFUNCTION as FUNCTION +from .nop import SymbolNOP as NOP +from .number import SymbolNUMBER as NUMBER +from .paramdecl import SymbolPARAMDECL as PARAMDECL +from .paramlist import SymbolPARAMLIST as PARAMLIST +from .sentence import SymbolSENTENCE as SENTENCE +from .string_ import SymbolSTRING as STRING +from .strslice import SymbolSTRSLICE as STRSLICE +from .type_ import SymbolBASICTYPE as BASICTYPE +from .type_ import SymbolTYPE as TYPE +from .type_ import SymbolTYPEREF as TYPEREF +from .typecast import SymbolTYPECAST as TYPECAST +from .unary import SymbolUNARY as UNARY +from .var import SymbolVAR as VAR +from .vararray import SymbolVARARRAY as VARARRAY +from .vardecl import SymbolVARDECL as VARDECL +from .label import SymbolLABEL as LABEL diff --git a/symbols/arglist.py b/symbols/arglist.py index d870ae77d..5a5f62684 100644 --- a/symbols/arglist.py +++ b/symbols/arglist.py @@ -9,14 +9,16 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol -from argument import SymbolARGUMENT +from .symbol_ import Symbol +from .argument import SymbolARGUMENT + class SymbolARGLIST(Symbol): - ''' Defines a list of arguments in a function call or array access - ''' + """ Defines a list of arguments in a function call or array access + """ + def __init__(self, *args): - Symbol.__init__(self, *args) + super(SymbolARGLIST, self).__init__(*args) @property def args(self): @@ -28,12 +30,12 @@ def args(self, value): assert isinstance(value, SymbolARGUMENT) self.appendChild(i) - def __getitem__(self, range): - return self.args[range] + def __getitem__(self, range_): + return self.args[range_] - def __setitem__(self, range, value): + def __setitem__(self, range_, value): assert isinstance(value, SymbolARGUMENT) - self.children[range] = value + self.children[range_] = value def __str__(self): return '(%s)' % (', '.join(str(x) for x in self.args)) @@ -46,8 +48,8 @@ def __len__(self): @classmethod def make_node(cls, node, *args): - ''' This will return a node with an argument_list. - ''' + """ This will return a node with an argument_list. + """ if node is None: node = cls() diff --git a/symbols/argument.py b/symbols/argument.py index aca4b83e5..502203252 100644 --- a/symbols/argument.py +++ b/symbols/argument.py @@ -10,21 +10,22 @@ # ---------------------------------------------------------------------- -from symbol_ import Symbol -from typecast import SymbolTYPECAST -from var import SymbolVAR +from .symbol_ import Symbol +from .typecast import SymbolTYPECAST +from .var import SymbolVAR from api.config import OPTIONS from api.constants import SCOPE class SymbolARGUMENT(Symbol): - ''' Defines an argument in a function call - ''' + """ Defines an argument in a function call + """ + def __init__(self, value, lineno, byref=None): - ''' Initializes the argument data. Byref must be set + """ Initializes the argument data. Byref must be set to True if this Argument is passed by reference. - ''' - Symbol.__init__(self, value) + """ + super(SymbolARGUMENT, self).__init__(value) self.lineno = lineno self.byref = byref if byref is not None else OPTIONS.byref.value @@ -75,17 +76,19 @@ def mangled(self): def size(self): return self.type_.size + def __hash__(self): + return id(self) + def __eq__(self, other): + assert isinstance(other, Symbol) if isinstance(other, SymbolARGUMENT): return self.value == other.value return self.value == other def typecast(self, type_): - ''' Test type casting to the argument expression. + """ Test type casting to the argument expression. On sucess 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) return self.value is not None - - diff --git a/symbols/arrayaccess.py b/symbols/arrayaccess.py index cf6dc2cf7..2a5b13ffb 100644 --- a/symbols/arrayaccess.py +++ b/symbols/arrayaccess.py @@ -16,14 +16,14 @@ from api.check import is_const from api.constants import TYPE -from call import SymbolCALL -from number import SymbolNUMBER as NUMBER -from typecast import SymbolTYPECAST as TYPECAST -from binary import SymbolBINARY as BINARY -from vararray import SymbolVARARRAY -from arglist import SymbolARGLIST - -from type_ import Type +from .call import SymbolCALL +from .number import SymbolNUMBER as NUMBER +from .typecast import SymbolTYPECAST as TYPECAST +from .binary import SymbolBINARY as BINARY +from .vararray import SymbolVARARRAY +from .arglist import SymbolARGLIST + +from .type_ import Type class SymbolARRAYACCESS(SymbolCALL): diff --git a/symbols/arraydecl.py b/symbols/arraydecl.py index 5c1764a72..20a22f1e6 100644 --- a/symbols/arraydecl.py +++ b/symbols/arraydecl.py @@ -10,7 +10,7 @@ # ---------------------------------------------------------------------- from api.constants import TYPE -from symbol_ import Symbol +from .symbol_ import Symbol class SymbolARRAYDECL(Symbol): diff --git a/symbols/arrayload.py b/symbols/arrayload.py index fb823b69a..bf5dc1bad 100644 --- a/symbols/arrayload.py +++ b/symbols/arrayload.py @@ -9,7 +9,7 @@ # the GNU General License # ---------------------------------------------------------------------- -from arrayaccess import SymbolARRAYACCESS +from .arrayaccess import SymbolARRAYACCESS class SymbolARRAYLOAD(SymbolARRAYACCESS): diff --git a/symbols/asm.py b/symbols/asm.py index bf32c295f..709e46db0 100644 --- a/symbols/asm.py +++ b/symbols/asm.py @@ -9,7 +9,7 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol +from .symbol_ import Symbol class SymbolASM(Symbol): diff --git a/symbols/binary.py b/symbols/binary.py index ec7693e70..d594e8ee0 100644 --- a/symbols/binary.py +++ b/symbols/binary.py @@ -9,12 +9,12 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol -from const import SymbolCONST -from number import SymbolNUMBER -from string_ import SymbolSTRING -from typecast import SymbolTYPECAST -from type_ import Type as TYPE +from .symbol_ import Symbol +from .const import SymbolCONST +from .number import SymbolNUMBER +from .string_ import SymbolSTRING +from .typecast import SymbolTYPECAST +from .type_ import Type as TYPE from api.check import common_type from api.check import is_const diff --git a/symbols/block.py b/symbols/block.py index d9c1b4428..0ecdc2149 100644 --- a/symbols/block.py +++ b/symbols/block.py @@ -10,8 +10,8 @@ # ---------------------------------------------------------------------- from api.check import is_null -from symbol_ import Symbol -from nop import SymbolNOP +from .symbol_ import Symbol + class SymbolBLOCK(Symbol): ''' Defines a block of code. @@ -20,7 +20,7 @@ def __init__(self, *nodes): Symbol.__init__(self, *(x for x in nodes if not is_null(x))) @classmethod - def make_node(clss, *args): + def make_node(cls, *args): ''' Creates a chain of code blocks. ''' args = [x for x in args if not is_null(x)] @@ -50,3 +50,6 @@ def __eq__(self, other): return False return len(self) == len(other) and all([x == y for x, y in zip(self, other)]) + + def __hash__(self): + return id(self) diff --git a/symbols/bound.py b/symbols/bound.py index 751faad28..20973c845 100644 --- a/symbols/bound.py +++ b/symbols/bound.py @@ -9,7 +9,7 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol +from .symbol_ import Symbol from api.check import is_static from api.errmsg import syntax_error diff --git a/symbols/boundlist.py b/symbols/boundlist.py index 3b093c2f4..2012043b8 100644 --- a/symbols/boundlist.py +++ b/symbols/boundlist.py @@ -9,8 +9,8 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol -from bound import SymbolBOUND +from .symbol_ import Symbol +from .bound import SymbolBOUND class SymbolBOUNDLIST(Symbol): diff --git a/symbols/builtin.py b/symbols/builtin.py index 13bd2ae55..83c2711af 100644 --- a/symbols/builtin.py +++ b/symbols/builtin.py @@ -9,10 +9,9 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol -from number import SymbolNUMBER -from string_ import SymbolSTRING -from type_ import SymbolTYPE +from .symbol_ import Symbol +from .number import SymbolNUMBER +from .type_ import SymbolTYPE from api.check import is_number from api.check import is_string diff --git a/symbols/call.py b/symbols/call.py index 9a3dc2a1e..9bdf14c35 100644 --- a/symbols/call.py +++ b/symbols/call.py @@ -12,13 +12,13 @@ import api.global_ as gl from api.check import check_call_arguments from api.constants import CLASS -from api.constants import KIND import api.errmsg as errmsg -from symbol_ import Symbol -from function import SymbolFUNCTION -from arglist import SymbolARGLIST -from type_ import Type +from .symbol_ import Symbol +from .function import SymbolFUNCTION +from .arglist import SymbolARGLIST +from .type_ import Type + class SymbolCALL(Symbol): ''' Defines function / procedure call. E.g. F(1, A + 2) diff --git a/symbols/const.py b/symbols/const.py index 98e1a1ab9..14a451d10 100644 --- a/symbols/const.py +++ b/symbols/const.py @@ -10,7 +10,7 @@ # ---------------------------------------------------------------------- import api.global_ as gl -from symbol_ import Symbol +from .symbol_ import Symbol # ---------------------------------------------------------------------- # CONST Symbol object @@ -22,7 +22,7 @@ class SymbolCONST(Symbol): or a more complex one like @label + 5) ''' def __init__(self, expr, lineno): - Symbol.__init__(self, expr) + super(SymbolCONST, self).__init__(expr) self.lineno = lineno self._t = gl.optemps.new_t() diff --git a/symbols/funccall.py b/symbols/funccall.py index 531217dcd..f4a73f9a4 100644 --- a/symbols/funccall.py +++ b/symbols/funccall.py @@ -9,7 +9,7 @@ # the GNU General License # ---------------------------------------------------------------------- -from call import SymbolCALL +from .call import SymbolCALL class SymbolFUNCCALL(SymbolCALL): diff --git a/symbols/funcdecl.py b/symbols/funcdecl.py index 3fa6b78b7..5ec28c92d 100644 --- a/symbols/funcdecl.py +++ b/symbols/funcdecl.py @@ -11,8 +11,8 @@ from api import global_ import api.symboltable -from symbol_ import Symbol -from function import SymbolFUNCTION +from .symbol_ import Symbol +from .function import SymbolFUNCTION class SymbolFUNCDECL(Symbol): diff --git a/symbols/function.py b/symbols/function.py index 14af56802..a79849b5f 100644 --- a/symbols/function.py +++ b/symbols/function.py @@ -12,9 +12,9 @@ from api.constants import CLASS from api.constants import KIND import api.errmsg -from var import SymbolVAR -from paramlist import SymbolPARAMLIST -from block import SymbolBLOCK +from .var import SymbolVAR +from .paramlist import SymbolPARAMLIST +from .block import SymbolBLOCK class SymbolFUNCTION(SymbolVAR): diff --git a/symbols/label.py b/symbols/label.py index 8d2c98511..001d54f85 100644 --- a/symbols/label.py +++ b/symbols/label.py @@ -10,8 +10,8 @@ # ---------------------------------------------------------------------- from api.constants import CLASS -from var import SymbolVAR -from symbol_ import Symbol +from .var import SymbolVAR +from .symbol_ import Symbol class SymbolLABEL(SymbolVAR): diff --git a/symbols/nop.py b/symbols/nop.py index fee88bd50..60ef82c3d 100644 --- a/symbols/nop.py +++ b/symbols/nop.py @@ -9,7 +9,7 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol +from .symbol_ import Symbol class SymbolNOP(Symbol): diff --git a/symbols/number.py b/symbols/number.py index 500a684ca..61d294228 100644 --- a/symbols/number.py +++ b/symbols/number.py @@ -12,20 +12,21 @@ import numbers from api.constants import CLASS -from symbol_ import Symbol -from type_ import SymbolTYPE -from type_ import Type as TYPE +from .symbol_ import Symbol +from .type_ import SymbolTYPE +from .type_ import Type as TYPE class SymbolNUMBER(Symbol): - ''' Defines an NUMBER symbol. - ''' + """ Defines an NUMBER symbol. + """ + def __init__(self, value, lineno, type_=None): assert lineno is not None assert type_ is None or isinstance(type_, SymbolTYPE) assert isinstance(value, numbers.Number) - Symbol.__init__(self) + super(Symbol, self).__init__() self.class_ = CLASS.const if int(value) == value: @@ -66,15 +67,48 @@ def __str__(self): def __repr__(self): return "%s:%s" % (self.type_, str(self)) + def __hash__(self): + return id(self) + @property def t(self): return str(self) - def __cmp__(self, other): + def __eq__(self, other): + if not isinstance(other, (numbers.Number, SymbolNUMBER)): + return False + + if isinstance(other, numbers.Number): + return self.value == other + + return self.value == other.value + + def __lt__(self, other): + assert isinstance(other, (numbers.Number, SymbolNUMBER)) + if isinstance(other, numbers.Number): - return self.value - other + return self.value < other + + return self.value < other.value + + def __gt__(self, other): + assert isinstance(other, (numbers.Number, SymbolNUMBER)) + + if isinstance(other, numbers.Number): + return self.value > other + + return self.value > other.value + + 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 + other, self.lineno) + def __sub__(self, other): + assert isinstance(other, (numbers.Number, SymbolNUMBER)) if isinstance(other, SymbolNUMBER): - return self.value - other.value + return SymbolNUMBER(self.value - other.value, self.lineno) - return cmp(self.value, other) + return SymbolNUMBER(self.value - other, self.lineno) diff --git a/symbols/paramdecl.py b/symbols/paramdecl.py index a775d2c02..8e1e74cb8 100644 --- a/symbols/paramdecl.py +++ b/symbols/paramdecl.py @@ -13,8 +13,8 @@ from api.constants import SCOPE from api.config import OPTIONS import api.global_ as gl -from type_ import SymbolBASICTYPE as BasicType -from var import SymbolVAR +from .type_ import SymbolBASICTYPE as BasicType +from .var import SymbolVAR class SymbolPARAMDECL(SymbolVAR): diff --git a/symbols/paramlist.py b/symbols/paramlist.py index aa33c3072..df7870af1 100644 --- a/symbols/paramlist.py +++ b/symbols/paramlist.py @@ -9,7 +9,7 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol +from .symbol_ import Symbol class SymbolPARAMLIST(Symbol): diff --git a/symbols/sentence.py b/symbols/sentence.py index 79f92eca6..65c87a542 100644 --- a/symbols/sentence.py +++ b/symbols/sentence.py @@ -9,7 +9,7 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol +from .symbol_ import Symbol from api.check import is_null diff --git a/symbols/string_.py b/symbols/string_.py index 2f6e019b5..4517e46f2 100644 --- a/symbols/string_.py +++ b/symbols/string_.py @@ -9,11 +9,12 @@ # the GNU General License # ---------------------------------------------------------------------- -import types +import six from api.constants import CLASS -from symbol_ import Symbol -from type_ import Type +from .symbol_ import Symbol +from .type_ import Type + class SymbolSTRING(Symbol): ''' Defines a string constant. @@ -43,26 +44,29 @@ def __repr__(self): return '"%s"' % str(self) def __eq__(self, other): - if type(other) in types.StringTypes: + if isinstance(other, six.string_types): return self.value == other assert isinstance(other, SymbolSTRING) return self.value == other.value def __gt__(self, other): - if type(other) in types.StringTypes: + if type(other) in six.string_types: return self.value > other assert isinstance(other, SymbolSTRING) return self.value > other.value def __lt__(self, other): - if type(other) in types.StringTypes: + if type(other) in six.string_types: return self.value < other assert isinstance(other, SymbolSTRING) return self.value < other.value + def __hash__(self): + return id(self) + def __ne__(self, other): return not self.__eq__(other) diff --git a/symbols/strslice.py b/symbols/strslice.py index f5f2fae61..24137a313 100644 --- a/symbols/strslice.py +++ b/symbols/strslice.py @@ -9,14 +9,12 @@ # the GNU General License # ---------------------------------------------------------------------- -from api.constants import TYPE - -from symbol_ import Symbol -from number import SymbolNUMBER as NUMBER -from typecast import SymbolTYPECAST as TYPECAST -from binary import SymbolBINARY as BINARY -from string_ import SymbolSTRING as STRING -from type_ import Type +from .symbol_ import Symbol +from .number import SymbolNUMBER as NUMBER +from .typecast import SymbolTYPECAST as TYPECAST +from .binary import SymbolBINARY as BINARY +from .string_ import SymbolSTRING as STRING +from .type_ import Type from api.config import OPTIONS from api.check import check_type @@ -24,6 +22,7 @@ import api.global_ as gl + class SymbolSTRSLICE(Symbol): ''' Defines a string slice ''' diff --git a/symbols/symbol_.py b/symbols/symbol_.py index c12ef0c3c..2a8adcf02 100644 --- a/symbols/symbol_.py +++ b/symbols/symbol_.py @@ -15,11 +15,11 @@ class Symbol(Ast): - ''' Symbol object to store everything related to + """ Symbol object to store everything related to a symbol. - ''' + """ def __init__(self, *children): - Ast.__init__(self) + super(Symbol, self).__init__() self._t = None for child in children: assert isinstance(child, Symbol) @@ -27,8 +27,8 @@ def __init__(self, *children): @property def token(self): - ''' token = AST Symbol class name, removing the 'Symbol' prefix. - ''' + """ token = AST Symbol class name, removing the 'Symbol' prefix. + """ return self.__class__.__name__[6:] # e.g. 'CALL', 'NUMBER', etc... def __str__(self): @@ -45,9 +45,9 @@ def t(self): return self._t def copy_attr(self, other): - ''' Copies all other attributes (not methods) + """ Copies all other attributes (not methods) from the other object to this instance. - ''' + """ if not isinstance(other, Symbol): return # Nothing done if not a Symbol object diff --git a/symbols/type_.py b/symbols/type_.py index be3a534d0..00ca591ae 100644 --- a/symbols/type_.py +++ b/symbols/type_.py @@ -12,8 +12,9 @@ from api.constants import TYPE from api.constants import CLASS from api.config import OPTIONS -from symbol_ import Symbol from api.decorator import classproperty +from .symbol_ import Symbol + class SymbolTYPE(Symbol): ''' A Type definition. Defines a type, diff --git a/symbols/typecast.py b/symbols/typecast.py index 3d15a2a86..8b95fe6e9 100644 --- a/symbols/typecast.py +++ b/symbols/typecast.py @@ -9,10 +9,10 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol -from type_ import SymbolTYPE -from type_ import Type as TYPE -from number import SymbolNUMBER +from .symbol_ import Symbol +from .type_ import SymbolTYPE +from .type_ import Type as TYPE +from .number import SymbolNUMBER from api.constants import CLASS from api.errmsg import syntax_error diff --git a/symbols/unary.py b/symbols/unary.py index dc67dee3a..4b7de1a63 100644 --- a/symbols/unary.py +++ b/symbols/unary.py @@ -9,12 +9,12 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol -from number import SymbolNUMBER -from string_ import SymbolSTRING -from typecast import SymbolTYPECAST -from type_ import SymbolTYPE -from type_ import Type as TYPE +from .symbol_ import Symbol +from .number import SymbolNUMBER +from .string_ import SymbolSTRING +from .typecast import SymbolTYPECAST +from .type_ import SymbolTYPE +from .type_ import Type as TYPE from api.check import is_number from api.check import is_string diff --git a/symbols/var.py b/symbols/var.py index 6463ba7fa..bd2d2f6bf 100644 --- a/symbols/var.py +++ b/symbols/var.py @@ -14,13 +14,15 @@ from api.constants import SCOPE from api.constants import KIND from api.constants import CLASS -from symbol_ import Symbol -from type_ import SymbolTYPE -from type_ import Type + +from .symbol_ import Symbol +from .type_ import SymbolTYPE # ---------------------------------------------------------------------- # IDentifier Symbol object # ---------------------------------------------------------------------- + + class SymbolVAR(Symbol): ''' Defines an VAR (Variable) symbol. These class and their children classes are also stored in the symbol diff --git a/symbols/vararray.py b/symbols/vararray.py index fdd84204f..c8a8566f2 100644 --- a/symbols/vararray.py +++ b/symbols/vararray.py @@ -9,11 +9,13 @@ # the GNU General License # ---------------------------------------------------------------------- +import functools + import api.global_ as gl from api.constants import TYPE from api.constants import CLASS -from var import SymbolVAR -from boundlist import SymbolBOUNDLIST +from .var import SymbolVAR +from .boundlist import SymbolBOUNDLIST class SymbolVARARRAY(SymbolVAR): @@ -36,7 +38,7 @@ def bounds(self, value): def count(self): ''' Total number of array cells ''' - return reduce(lambda x, y: x * y, (x.count for x in self.bounds)) + return functools.reduce(lambda x, y: x * y, (x.count for x in self.bounds)) @property def size(self): diff --git a/symbols/vardecl.py b/symbols/vardecl.py index 66aa03cf9..4e499bf93 100644 --- a/symbols/vardecl.py +++ b/symbols/vardecl.py @@ -9,7 +9,7 @@ # the GNU General License # ---------------------------------------------------------------------- -from symbol_ import Symbol +from .symbol_ import Symbol class SymbolVARDECL(Symbol): diff --git a/tests/api/test_symbolTable.py b/tests/api/test_symbolTable.py index caafae01c..e718b6564 100644 --- a/tests/api/test_symbolTable.py +++ b/tests/api/test_symbolTable.py @@ -3,7 +3,7 @@ import unittest from unittest import TestCase -from StringIO import StringIO +from six import StringIO # Initialize import syspath import __init__ diff --git a/tests/functional/19.asm b/tests/functional/19.asm index 19e99c714..70ab7c141 100644 --- a/tests/functional/19.asm +++ b/tests/functional/19.asm @@ -80,7 +80,7 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "tan.asm" +#line 1 "acos.asm" #line 1 "stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -127,91 +127,91 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK xor a ld b, a jp __FPSTACK_PUSH -#line 2 "tan.asm" +#line 2 "acos.asm" -TAN: ; Computes TAN using ROM FP-CALC +ACOS: ; Computes ACOS using ROM FP-CALC call __FPSTACK_PUSH rst 28h ; ROM CALC - defb 21h ; TAN + defb 23h ; ACOS defb 38h ; END CALC jp __FPSTACK_POP #line 72 "19.bas" -#line 1 "exp.asm" +#line 1 "asin.asm" -EXP: ; Computes e^n using ROM FP-CALC +ASIN: ; Computes ASIN using ROM FP-CALC call __FPSTACK_PUSH rst 28h ; ROM CALC - defb 26h ; E^n + defb 22h ; ASIN defb 38h ; END CALC jp __FPSTACK_POP #line 73 "19.bas" -#line 1 "sin.asm" +#line 1 "atan.asm" -SIN: ; Computes SIN using ROM FP-CALC +ATAN: ; Computes ATAN using ROM FP-CALC call __FPSTACK_PUSH rst 28h ; ROM CALC - defb 1Fh + defb 24h ; ATAN defb 38h ; END CALC jp __FPSTACK_POP #line 74 "19.bas" -#line 1 "acos.asm" +#line 1 "cos.asm" -ACOS: ; Computes ACOS using ROM FP-CALC +COS: ; Computes COS using ROM FP-CALC call __FPSTACK_PUSH rst 28h ; ROM CALC - defb 23h ; ACOS + defb 20h ; COS defb 38h ; END CALC jp __FPSTACK_POP #line 75 "19.bas" -#line 1 "atan.asm" +#line 1 "exp.asm" -ATAN: ; Computes ATAN using ROM FP-CALC +EXP: ; Computes e^n using ROM FP-CALC call __FPSTACK_PUSH rst 28h ; ROM CALC - defb 24h ; ATAN + defb 26h ; E^n defb 38h ; END CALC jp __FPSTACK_POP #line 76 "19.bas" -#line 1 "cos.asm" +#line 1 "logn.asm" -COS: ; Computes COS using ROM FP-CALC +LN: ; Computes Ln(x) using ROM FP-CALC call __FPSTACK_PUSH rst 28h ; ROM CALC - defb 20h ; COS + defb 20h ; 25h defb 38h ; END CALC jp __FPSTACK_POP #line 77 "19.bas" -#line 1 "asin.asm" +#line 1 "sin.asm" -ASIN: ; Computes ASIN using ROM FP-CALC +SIN: ; Computes SIN using ROM FP-CALC call __FPSTACK_PUSH rst 28h ; ROM CALC - defb 22h ; ASIN + defb 1Fh defb 38h ; END CALC jp __FPSTACK_POP @@ -260,14 +260,14 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL ret #line 80 "19.bas" -#line 1 "logn.asm" +#line 1 "tan.asm" -LN: ; Computes Ln(x) using ROM FP-CALC +TAN: ; Computes TAN using ROM FP-CALC call __FPSTACK_PUSH rst 28h ; ROM CALC - defb 20h ; 25h + defb 21h ; TAN defb 38h ; END CALC jp __FPSTACK_POP diff --git a/tests/functional/27.asm b/tests/functional/27.asm index 633366223..7618aa024 100644 --- a/tests/functional/27.asm +++ b/tests/functional/27.asm @@ -398,9 +398,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/28.asm b/tests/functional/28.asm index bdb03ea18..b5d8a2c3d 100644 --- a/tests/functional/28.asm +++ b/tests/functional/28.asm @@ -401,9 +401,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/29.asm b/tests/functional/29.asm index 409bf74d1..e42ed147e 100644 --- a/tests/functional/29.asm +++ b/tests/functional/29.asm @@ -50,7 +50,6 @@ __LABEL0: DEFB 53h DEFB 49h DEFB 43h -#line 1 "strcat.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -324,9 +323,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -401,149 +400,6 @@ __MEM_SUBTRACT: 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 39 "29.bas" #line 1 "storestr.asm" ; vim:ts=4:et:sw=4 @@ -1037,7 +893,151 @@ __STORE_STR: 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$ + + 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 41 "29.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/46.asm b/tests/functional/46.asm index 1f55ffa0d..46afdab1f 100644 --- a/tests/functional/46.asm +++ b/tests/functional/46.asm @@ -116,7 +116,7 @@ __MUL16NOADD: #line 20 "array.asm" -#line 24 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" __ARRAY: PROC @@ -138,12 +138,12 @@ __ARRAY: ld hl, 0 ; BC = Offset "accumulator" -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOOP: pop bc ; Get next index (Ai) from the stack -#line 60 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" add hl, bc ; Adds current index @@ -161,10 +161,10 @@ LOOP: exx pop de ; DE = Max bound Number (i-th dimension) -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP ARRAY_END: @@ -175,7 +175,7 @@ ARRAY_END: push de exx -#line 100 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP ex de, hl @@ -206,7 +206,7 @@ ARRAY_SIZE_LOOP: ;add hl, de ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" pop de add hl, de ; Adds element start diff --git a/tests/functional/47.asm b/tests/functional/47.asm index 32237cccb..b3925394d 100644 --- a/tests/functional/47.asm +++ b/tests/functional/47.asm @@ -143,7 +143,7 @@ __MUL16NOADD: #line 20 "array.asm" -#line 24 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" __ARRAY: PROC @@ -165,12 +165,12 @@ __ARRAY: ld hl, 0 ; BC = Offset "accumulator" -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOOP: pop bc ; Get next index (Ai) from the stack -#line 60 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" add hl, bc ; Adds current index @@ -188,10 +188,10 @@ LOOP: exx pop de ; DE = Max bound Number (i-th dimension) -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP ARRAY_END: @@ -202,7 +202,7 @@ ARRAY_END: push de exx -#line 100 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP ex de, hl @@ -233,7 +233,7 @@ ARRAY_SIZE_LOOP: ;add hl, de ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" pop de add hl, de ; Adds element start diff --git a/tests/functional/48.asm b/tests/functional/48.asm index d1b05f4f8..f6eaf5ede 100644 --- a/tests/functional/48.asm +++ b/tests/functional/48.asm @@ -409,9 +409,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/49.asm b/tests/functional/49.asm index 2256babaa..e8061dc86 100644 --- a/tests/functional/49.asm +++ b/tests/functional/49.asm @@ -409,9 +409,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/55.asm b/tests/functional/55.asm index 00c398974..4c649bd81 100644 --- a/tests/functional/55.asm +++ b/tests/functional/55.asm @@ -111,7 +111,7 @@ __MUL16NOADD: #line 20 "array.asm" -#line 24 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" __ARRAY: PROC @@ -133,12 +133,12 @@ __ARRAY: ld hl, 0 ; BC = Offset "accumulator" -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOOP: pop bc ; Get next index (Ai) from the stack -#line 60 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" add hl, bc ; Adds current index @@ -156,10 +156,10 @@ LOOP: exx pop de ; DE = Max bound Number (i-th dimension) -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP ARRAY_END: @@ -170,7 +170,7 @@ ARRAY_END: push de exx -#line 100 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP ex de, hl @@ -201,7 +201,7 @@ ARRAY_SIZE_LOOP: ;add hl, de ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" pop de add hl, de ; Adds element start diff --git a/tests/functional/addstr.asm b/tests/functional/addstr.asm index 9905d9541..f1ee0c226 100644 --- a/tests/functional/addstr.asm +++ b/tests/functional/addstr.asm @@ -64,8 +64,15 @@ __LABEL0: __LABEL1: DEFW 0001h DEFB 31h -#line 1 "strcat.asm" -#line 1 "alloc.asm" +#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) @@ -120,7 +127,7 @@ __LABEL1: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -133,47 +140,6 @@ __LABEL1: ; 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_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 @@ -290,17 +256,282 @@ __MEM_INIT2: 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 (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 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) +; 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + - ld a, 201 - ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again - ret + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret - ENDP + ; 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 70 "alloc.asm" ; --------------------------------------------------------------------- @@ -338,9 +569,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -558,237 +789,6 @@ __STRCATEND: ENDP -#line 53 "addstr.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 54 "addstr.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/aloadstr0.asm b/tests/functional/aloadstr0.asm index 3a78335e4..b33cdaa92 100644 --- a/tests/functional/aloadstr0.asm +++ b/tests/functional/aloadstr0.asm @@ -34,15 +34,8 @@ __END_PROGRAM: ret __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" +#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) @@ -97,7 +90,7 @@ __CALL_BACK__: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -110,6 +103,47 @@ __CALL_BACK__: ; 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_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 @@ -236,163 +270,174 @@ __MEM_INIT2: ENDP -#line 69 "free.asm" +#line 70 "alloc.asm" + ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory + ; MEM_ALLOC + ; Allocates a block of memory in the heap. ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done + ; 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_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) PROC - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - ld a, h - or l - ret z ; Return if NULL pointer + TEMP EQU TEMP0 + 1 - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + ld hl, 0 + ld (TEMP), hl +__MEM_START: ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start - -__MEM_LOOP2: + 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" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) inc hl - inc hl ; Next block ptr + 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) ; 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 + 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 - dec hl - - ld (hl), c + 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 (hl), b ; (DE) <- BC + 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 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 + ld (hl), e inc hl - ld (hl), d - ; --- LINKED ; HL = &(BC->next) + 2 - - call __MEM_JOIN_TEST - pop hl + ld (hl), d ; LINKED + pop hl ; Returning block. + + ret -__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them - ; hl = Ptr to current block + 2 - ld d, (hl) +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl dec hl - ld e, (hl) - dec hl - ld b, (hl) ; Loads block length into BC + ld (hl), d dec hl - ld c, (hl) ; + ld (hl), e ; Store new block length - 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 + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack -__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) + ld (hl), c ; Store length on its 1st word inc hl + ld (hl), b + inc hl ; Return hl + ret + + ENDP - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length - ld b, h - ld c, l ; BC = Total Length +#line 2 "loadstr.asm" - ex de, hl - ld e, (hl) - inc hl - ld d, (hl) ; DE = block->next + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL - 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 +__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 - ENDP +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL -#line 9 "storestr2.asm" + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(a$) -__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) - push ix - pop hl - add hl, bc + inc bc + inc bc ; BC = LEN(a$) + 2 (two bytes for length) -__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. + push hl + push bc + call __MEM_ALLOC + pop bc ; Recover length + pop de ; Recover origin - ret + 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 23 "aloadstr0.bas" -#line 1 "loadstr.asm" -#line 1 "alloc.asm" +#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) @@ -447,7 +492,7 @@ __STORE_STR2: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -460,205 +505,160 @@ __STORE_STR2: ; 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 + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- - ; Error code definitions (as in ZX spectrum manual) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC -; Set error code with: - ; ld a, ERROR_CODE - ; ld (ERR_NR), a + 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 - 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_InvalidFileName EQU 14 - ERROR_InvalidColour EQU 19 - ERROR_BreakIntoProgram EQU 20 - ERROR_TapeLoadingErr EQU 26 + 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 - ; Raises error using RST #8 -__ERROR: - ld (__ERROR_CODE), a - rst 8 -__ERROR_CODE: - nop - ret +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr - ; 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" + 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 - ; --------------------------------------------------------------------- - ; 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) - ; --------------------------------------------------------------------- + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) - PROC +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC - TEMP EQU TEMP0 + 1 + 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 - ld hl, 0 - ld (TEMP), hl + call __MEM_JOIN_TEST + pop 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/spyder/zxbasic/library-asm/alloc.asm" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) - inc hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 ld d, (hl) - inc hl ; DE = Block Length + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; - 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 + 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 - ld (TEMP), 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) + + ld e, (hl) ; DE -> block->next->length 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 + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + + ld b, h + ld c, l ; BC = Total Length -__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 e, (hl) + inc hl + ld d, (hl) ; DE = block->next - ld (hl), c ; Store length on its 1st word + pop hl ; Recovers Pointer to block + ld (hl), c inc hl - ld (hl), b - inc hl ; Return hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved ret - - ENDP - -#line 2 "loadstr.asm" + ENDP - ; Loads a string (ptr) from HL - ; and duplicates it on dynamic memory again - ; Finally, it returns result pointer in HL +#line 9 "storestr2.asm" -__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 +__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) + push ix + pop hl + add hl, bc -__LOADSTR: ; __FASTCALL__ entry - ld a, h - or l - ret z ; Return if NULL +__ISTORE_STR2: + ld c, (hl) ; Dereferences HL + inc hl + ld h, (hl) + ld l, c ; HL = *HL (real string variable address) - ld c, (hl) - inc hl - ld b, (hl) - dec hl ; BC = LEN(a$) +__STORE_STR2: + push hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = *HL (real string address) - inc bc - inc bc ; BC = LEN(a$) + 2 (two bytes for length) + push de + call __MEM_FREE + pop de - push hl - push bc - call __MEM_ALLOC - pop bc ; Recover length - pop de ; Recover origin + 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. - ld a, h - or l - ret z ; Return if NULL (No memory) + ret - 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 "aloadstr0.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/aloadstr1.asm b/tests/functional/aloadstr1.asm index 1e5bc6151..12f0ee994 100644 --- a/tests/functional/aloadstr1.asm +++ b/tests/functional/aloadstr1.asm @@ -39,15 +39,209 @@ __END_PROGRAM: ret __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 "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> -#line 1 "free.asm" + ; 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/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" + +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +LOOP: + pop bc ; Get next index (Ai) from the stack + +#line 60 "/Users/boriel/Documents/src/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) + +#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 + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 100 "/Users/boriel/Documents/src/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 131 "/Users/boriel/Documents/src/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 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) @@ -102,7 +296,7 @@ __CALL_BACK__: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -115,6 +309,47 @@ __CALL_BACK__: ; 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_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 @@ -241,478 +476,12 @@ __MEM_INIT2: ENDP -#line 69 "free.asm" +#line 70 "alloc.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 28 "aloadstr1.bas" -#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/spyder/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" - -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - -LOOP: - pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/spyder/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) - -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - ;call __MUL16_FAST ; HL *= DE - call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - jp LOOP - -ARRAY_END: - ld e, (hl) - inc hl - ld d, c ; C = 0 => DE = E = Element size - push hl - push de - exx - -#line 100 "/Users/boriel/Documents/src/spyder/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 131 "/Users/boriel/Documents/src/spyder/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 29 "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) -; 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_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. + ; MEM_ALLOC + ; Allocates a block of memory in the heap. ; ; Parameters ; BC = Length of requested memory block @@ -745,9 +514,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -865,6 +634,237 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result 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) +; 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 30 "aloadstr1.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/arden2.asm b/tests/functional/arden2.asm index 24b120323..866fa9884 100644 --- a/tests/functional/arden2.asm +++ b/tests/functional/arden2.asm @@ -44,10 +44,6 @@ __END_PROGRAM: ret __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 @@ -321,9 +317,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -398,7 +394,12 @@ __MEM_SUBTRACT: ENDP -#line 5 "chr.asm" +#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. @@ -470,151 +471,6 @@ __CHR_END: ENDP -#line 33 "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$ - - 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 34 "arden2.bas" #line 1 "storestr2.asm" ; Similar to __STORE_STR, but this one is called when @@ -847,7 +703,151 @@ __STORE_STR2: 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$ + + 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 36 "arden2.bas" ZXBASIC_USER_DATA: _c: diff --git a/tests/functional/array03.asm b/tests/functional/array03.asm index d6dbe2851..bb20aaf13 100644 --- a/tests/functional/array03.asm +++ b/tests/functional/array03.asm @@ -109,7 +109,7 @@ __MUL16NOADD: #line 20 "array.asm" -#line 24 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" __ARRAY: PROC @@ -131,12 +131,12 @@ __ARRAY: ld hl, 0 ; BC = Offset "accumulator" -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOOP: pop bc ; Get next index (Ai) from the stack -#line 60 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" add hl, bc ; Adds current index @@ -154,10 +154,10 @@ LOOP: exx pop de ; DE = Max bound Number (i-th dimension) -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP ARRAY_END: @@ -168,7 +168,7 @@ ARRAY_END: push de exx -#line 100 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP ex de, hl @@ -199,7 +199,7 @@ ARRAY_SIZE_LOOP: ;add hl, de ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" pop de add hl, de ; Adds element start diff --git a/tests/functional/array06.asm b/tests/functional/array06.asm index 880fa993e..3ca57bcaa 100644 --- a/tests/functional/array06.asm +++ b/tests/functional/array06.asm @@ -109,7 +109,7 @@ __MUL16NOADD: #line 20 "array.asm" -#line 24 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" __ARRAY: PROC @@ -131,12 +131,12 @@ __ARRAY: ld hl, 0 ; BC = Offset "accumulator" -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOOP: pop bc ; Get next index (Ai) from the stack -#line 60 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" add hl, bc ; Adds current index @@ -154,10 +154,10 @@ LOOP: exx pop de ; DE = Max bound Number (i-th dimension) -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP ARRAY_END: @@ -168,7 +168,7 @@ ARRAY_END: push de exx -#line 100 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP ex de, hl @@ -199,7 +199,7 @@ ARRAY_SIZE_LOOP: ;add hl, de ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" pop de add hl, de ; Adds element start diff --git a/tests/functional/array07.asm b/tests/functional/array07.asm index 43fd11d17..fd0837b3b 100644 --- a/tests/functional/array07.asm +++ b/tests/functional/array07.asm @@ -57,21 +57,208 @@ _test__leave: ex (sp), hl exx ret -#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 "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 "strcpy.asm" -#line 1 "realloc.asm" +#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/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" + +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +LOOP: + pop bc ; Get next index (Ai) from the stack + +#line 60 "/Users/boriel/Documents/src/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) + +#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 + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 100 "/Users/boriel/Documents/src/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 131 "/Users/boriel/Documents/src/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 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) @@ -139,49 +326,7 @@ _test__leave: ; 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_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" +#line 1 "heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -236,76 +381,7 @@ __STOP: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -376,123 +452,144 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" - +#line 69 "free.asm" ; --------------------------------------------------------------------- - ; MEM_ALLOC - ; Allocates a block of memory in the heap. - ; - ; Parameters - ; BC = Length of requested memory block + ; MEM_FREE + ; Frees a block of memory ; -; Returns: - ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) - ; if the block could not be allocated (out of memory) +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done ; --------------------------------------------------------------------- -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified PROC - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - TEMP EQU TEMP0 + 1 + ld a, h + or l + ret z ; Return if NULL pointer - ld hl, 0 - ld (TEMP), hl + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__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/spyder/zxbasic/library-asm/alloc.asm" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) + +__MEM_LOOP2: 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 + inc hl ; Next block ptr - ex de, hl ld e, (hl) inc hl - ld d, (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 - 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) + dec hl + + ld (hl), c 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), b ; (DE) <- BC - ld (hl), e + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) - ex de, hl + 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 (hl), d + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC dec hl - ld (hl), e ; Store new block length + ld c, (hl) ; - add hl, de ; New length + DE => free-block start - pop de ; Remove previous HL off the stack + 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 - ld (hl), c ; Store length on its 1st word +__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 (hl), b - inc hl ; Return 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 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$) + ; 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 71 "realloc.asm" -#line 1 "free.asm" + +#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) @@ -561,128 +658,233 @@ __MEM_SUBTRACT: ; They will be added automatically if needed. +#line 1 "error.asm" + ; Simple error control routines +; vim:ts=4:et: - ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory + 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_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 ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done - ; --------------------------------------------------------------------- + ; 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 -MEM_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified - PROC + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +; 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)| + ; +----------------+ - ld a, h - or l - ret z ; Return if NULL pointer - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + ; When 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). - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - 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 + ; --------------------------------------------------------------------- + ; 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) + ; --------------------------------------------------------------------- - 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 +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC - call __MEM_JOIN_TEST - pop hl + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 -__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 + TEMP EQU TEMP0 + 1 -__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 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 - ld e, (hl) ; DE -> block->next->length +__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" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) 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 + 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) ; DE = block->next - - pop hl ; Recovers Pointer to block - ld (hl), c - inc hl - ld (hl), b ; Length Saved + 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 ; Next saved + 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 72 "realloc.asm" + +#line 71 "realloc.asm" + ; --------------------------------------------------------------------- @@ -898,208 +1100,6 @@ __STORE_STR: pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret -#line 46 "array07.bas" - -#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/spyder/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" - -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - -LOOP: - pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/spyder/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) - -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - ;call __MUL16_FAST ; HL *= DE - call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - jp LOOP - -ARRAY_END: - ld e, (hl) - inc hl - ld d, c ; C = 0 => DE = E = Element size - push hl - push de - exx - -#line 100 "/Users/boriel/Documents/src/spyder/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 131 "/Users/boriel/Documents/src/spyder/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 "array07.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/array09.asm b/tests/functional/array09.asm index 23d06d5d9..dbe33c68b 100644 --- a/tests/functional/array09.asm +++ b/tests/functional/array09.asm @@ -35,6 +35,207 @@ __END_PROGRAM: 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 "/Users/boriel/Documents/src/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" + +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +LOOP: + pop bc ; Get next index (Ai) from the stack + +#line 60 "/Users/boriel/Documents/src/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) + +#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 + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 100 "/Users/boriel/Documents/src/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 131 "/Users/boriel/Documents/src/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 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 @@ -392,9 +593,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -876,207 +1077,6 @@ __STORE_STR: pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret -#line 24 "array09.bas" -#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/src/spyder/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" - -#line 48 "/Users/boriel/src/spyder/zxbasic/library-asm/array.asm" - -LOOP: - pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/src/spyder/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) - -#line 80 "/Users/boriel/src/spyder/zxbasic/library-asm/array.asm" - ;call __MUL16_FAST ; HL *= DE - call __FNMUL -#line 86 "/Users/boriel/src/spyder/zxbasic/library-asm/array.asm" - jp LOOP - -ARRAY_END: - ld e, (hl) - inc hl - ld d, c ; C = 0 => DE = E = Element size - push hl - push de - exx - -#line 100 "/Users/boriel/src/spyder/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 131 "/Users/boriel/src/spyder/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 25 "array09.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/array10.asm b/tests/functional/array10.asm index da53387d8..817147528 100644 --- a/tests/functional/array10.asm +++ b/tests/functional/array10.asm @@ -142,7 +142,7 @@ __MUL16NOADD: #line 20 "array.asm" -#line 24 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 24 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" __ARRAY: PROC @@ -164,12 +164,12 @@ __ARRAY: ld hl, 0 ; BC = Offset "accumulator" -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOOP: pop bc ; Get next index (Ai) from the stack -#line 60 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 60 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" add hl, bc ; Adds current index @@ -187,10 +187,10 @@ LOOP: exx pop de ; DE = Max bound Number (i-th dimension) -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 80 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" ;call __MUL16_FAST ; HL *= DE call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 86 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" jp LOOP ARRAY_END: @@ -201,7 +201,7 @@ ARRAY_END: push de exx -#line 100 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 100 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" LOCAL ARRAY_SIZE_LOOP ex de, hl @@ -232,7 +232,7 @@ ARRAY_SIZE_LOOP: ;add hl, de ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +#line 131 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" pop de add hl, de ; Adds element start diff --git a/tests/functional/astore16.asm b/tests/functional/astore16.asm index 4b72458c6..6e75a7fd0 100644 --- a/tests/functional/astore16.asm +++ b/tests/functional/astore16.asm @@ -62,6 +62,207 @@ __END_PROGRAM: 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 "/Users/boriel/Documents/src/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" + +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +LOOP: + pop bc ; Get next index (Ai) from the stack + +#line 60 "/Users/boriel/Documents/src/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) + +#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 + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 100 "/Users/boriel/Documents/src/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 131 "/Users/boriel/Documents/src/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 53 "astore16.bas" #line 1 "print.asm" ; vim:ts=4:sw=4:et: ; PRINT command routine @@ -405,7 +606,7 @@ BRIGHT_TMP: #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" +#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" @@ -464,7 +665,7 @@ TABLE: and (hl) ; OVER 2 MODE or (hl) ; OVER 3 MODE -#line 65 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" +#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" __REFRESH_TMP: ld a, (hl) @@ -1167,7 +1368,7 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 53 "astore16.bas" +#line 54 "astore16.bas" #line 1 "printu16.asm" #line 1 "printi16.asm" #line 1 "printnum.asm" @@ -1364,207 +1565,6 @@ __PRINTU_LOOP: #line 2 "printu16.asm" -#line 54 "astore16.bas" -#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/src/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" - -#line 48 "/Users/boriel/src/zxbasic/library-asm/array.asm" - -LOOP: - pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/src/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) - -#line 80 "/Users/boriel/src/zxbasic/library-asm/array.asm" - ;call __MUL16_FAST ; HL *= DE - call __FNMUL -#line 86 "/Users/boriel/src/zxbasic/library-asm/array.asm" - jp LOOP - -ARRAY_END: - ld e, (hl) - inc hl - ld d, c ; C = 0 => DE = E = Element size - push hl - push de - exx - -#line 100 "/Users/boriel/src/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 131 "/Users/boriel/src/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 55 "astore16.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/attr.asm b/tests/functional/attr.asm index 7f7cc2782..9880b812d 100644 --- a/tests/functional/attr.asm +++ b/tests/functional/attr.asm @@ -45,7 +45,7 @@ __CALL_BACK__: ; 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/spyder/zxbasic/library-asm/copy_attr.asm" +#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" #line 1 "const.asm" ; Global constants @@ -82,9 +82,9 @@ COPY_ATTR: __SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) -#line 63 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/copy_attr.asm" +#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/copy_attr.asm" +#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" __REFRESH_TMP: ld a, (hl) @@ -126,6 +126,34 @@ BOLD_TMP: 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) + 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 35 "attr.bas" #line 1 "ink.asm" ; Sets ink color in ATTR_P permanently ; Parameter: Paper color in A register @@ -170,55 +198,7 @@ INK_TMP: ENDP -#line 34 "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 - 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 35 "attr.bas" +#line 36 "attr.bas" #line 1 "over.asm" ; Sets OVER flag in P_FLAG permanently ; Parameter: OVER flag in bit 0 of A register @@ -265,35 +245,55 @@ OVER_TMP: ENDP -#line 36 "attr.bas" -#line 1 "flash.asm" - ; Sets flash flag in ATTR_P permanently +#line 37 "attr.bas" +#line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently ; Parameter: Paper color in A register -FLASH: +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + 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 +__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 07Fh ; Clears previous value + 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 FLASH flag passed in A register in the ATTR_T variable -FLASH_TMP: + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: ld de, ATTR_T - jr __SET_FLASH - -#line 37 "attr.bas" + jp __SET_PAPER + ENDP +#line 38 "attr.bas" ZXBASIC_USER_DATA: ; Defines DATA END --> HEAP size is 0 diff --git a/tests/functional/bxor16.asm b/tests/functional/bxor16.asm index 84ad8d69d..1c5b6241e 100644 --- a/tests/functional/bxor16.asm +++ b/tests/functional/bxor16.asm @@ -55,23 +55,6 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#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 47 "bxor16.bas" #line 1 "bxor16.asm" ; vim:ts=4:et: ; FASTCALL bitwise xor 16 version. @@ -92,6 +75,23 @@ __BXOR16: 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 + ld l, a + ld a, h + cpl + ld h, a + inc hl + ret + #line 48 "bxor16.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/byref32.asm b/tests/functional/byref32.asm index 9d2a69184..1b59e1014 100644 --- a/tests/functional/byref32.asm +++ b/tests/functional/byref32.asm @@ -60,6 +60,25 @@ _test__leave: ex (sp), hl 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) + inc hl + ld d, (hl) + inc hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ex de, hl + ret + +#line 52 "byref32.bas" #line 1 "pistore32.asm" #line 1 "store32.asm" __PISTORE32: @@ -88,25 +107,6 @@ __STORE32: ; Stores the given integer in DEBC at address HL #line 2 "pistore32.asm" ; The content of this file has been moved to "store32.asm" -#line 52 "byref32.bas" -#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 53 "byref32.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/chr0.asm b/tests/functional/chr0.asm index da141f7bf..77b4db652 100644 --- a/tests/functional/chr0.asm +++ b/tests/functional/chr0.asm @@ -319,9 +319,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/chr1.asm b/tests/functional/chr1.asm index c1f5ba279..4434ec7b6 100644 --- a/tests/functional/chr1.asm +++ b/tests/functional/chr1.asm @@ -394,9 +394,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/circle.asm b/tests/functional/circle.asm index 13700cdc1..4f0338724 100644 --- a/tests/functional/circle.asm +++ b/tests/functional/circle.asm @@ -84,115 +84,6 @@ __END_PROGRAM: ret __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 - - 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, __NEG32 ; Negates DEHL - - ret - - ENDP - - -__FTOU8: ; Converts float in C ED LH to Unsigned byte in A - call __FTOU32REG - ld a, l - ret - -#line 76 "circle.bas" #line 1 "circle.asm" ; Bresenham's like circle algorithm ; best known as Middle Point Circle drawing algorithm @@ -647,6 +538,115 @@ __CIRCLE_PLOT: 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 + + 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, __NEG32 ; Negates DEHL + + ret + + 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: diff --git a/tests/functional/code00.asm b/tests/functional/code00.asm index 405676df1..921aa86e0 100644 --- a/tests/functional/code00.asm +++ b/tests/functional/code00.asm @@ -42,7 +42,7 @@ __CALL_BACK__: DEFW 0 __LABEL0: DEFW 0000h -#line 1 "loadstr.asm" +#line 1 "load.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -316,9 +316,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/src/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/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/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -393,52 +393,7 @@ __MEM_SUBTRACT: 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 "code00.bas" -#line 1 "load.asm" - +#line 2 "load.asm" #line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -931,7 +886,7 @@ BRIGHT_TMP: ; 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/library-asm/copy_attr.asm" @@ -957,9 +912,9 @@ COPY_ATTR: __SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) -#line 63 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" +#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" +#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" __REFRESH_TMP: ld a, (hl) @@ -1941,6 +1896,51 @@ PRINT_TAPE_MSG: 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 + 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 31 "code00.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/code01.asm b/tests/functional/code01.asm index a293bb3eb..ff67b41b0 100644 --- a/tests/functional/code01.asm +++ b/tests/functional/code01.asm @@ -42,7 +42,7 @@ __CALL_BACK__: DEFW 0 __LABEL0: DEFW 0000h -#line 1 "loadstr.asm" +#line 1 "load.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -316,9 +316,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/src/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/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/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -393,52 +393,7 @@ __MEM_SUBTRACT: 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 "code01.bas" -#line 1 "load.asm" - +#line 2 "load.asm" #line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -931,7 +886,7 @@ BRIGHT_TMP: ; 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/library-asm/copy_attr.asm" @@ -957,9 +912,9 @@ COPY_ATTR: __SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) -#line 63 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" +#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" +#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" __REFRESH_TMP: ld a, (hl) @@ -1941,6 +1896,51 @@ PRINT_TAPE_MSG: 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 + 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 31 "code01.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/code02.asm b/tests/functional/code02.asm index 72ddd3c21..f40f7f797 100644 --- a/tests/functional/code02.asm +++ b/tests/functional/code02.asm @@ -42,7 +42,7 @@ __CALL_BACK__: DEFW 0 __LABEL0: DEFW 0000h -#line 1 "loadstr.asm" +#line 1 "load.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -316,9 +316,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/src/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/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/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -393,52 +393,7 @@ __MEM_SUBTRACT: 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 "code02.bas" -#line 1 "load.asm" - +#line 2 "load.asm" #line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -931,7 +886,7 @@ BRIGHT_TMP: ; 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/library-asm/copy_attr.asm" @@ -957,9 +912,9 @@ COPY_ATTR: __SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) -#line 63 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" +#line 63 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" ret -#line 65 "/Users/boriel/src/zxbasic/library-asm/copy_attr.asm" +#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" __REFRESH_TMP: ld a, (hl) @@ -1941,6 +1896,51 @@ PRINT_TAPE_MSG: 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 + 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 31 "code02.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/codecrash2.asm b/tests/functional/codecrash2.asm index c0e9e8e8b..f69bf04b8 100644 --- a/tests/functional/codecrash2.asm +++ b/tests/functional/codecrash2.asm @@ -35,8 +35,9 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "strcat.asm" -#line 1 "alloc.asm" +#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) @@ -91,7 +92,7 @@ __CALL_BACK__: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -104,47 +105,6 @@ __CALL_BACK__: ; 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_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 @@ -261,17 +221,284 @@ __MEM_INIT2: 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 (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 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) +; 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + - ld a, 201 - ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again - ret + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret - ENDP + ; 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 70 "alloc.asm" ; --------------------------------------------------------------------- @@ -309,9 +536,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -529,233 +756,6 @@ __STRCATEND: ENDP -#line 24 "codecrash2.bas" -#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) -; 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 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 25 "codecrash2.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/codecrash3.asm b/tests/functional/codecrash3.asm index fe6272c68..7ff4abc2a 100644 --- a/tests/functional/codecrash3.asm +++ b/tests/functional/codecrash3.asm @@ -33,13 +33,9 @@ __END_PROGRAM: ret __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" +#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) @@ -94,7 +90,7 @@ __CALL_BACK__: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -107,47 +103,6 @@ __CALL_BACK__: ; 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_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 @@ -274,180 +229,170 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" - +#line 69 "free.asm" ; --------------------------------------------------------------------- - ; MEM_ALLOC - ; Allocates a block of memory in the heap. - ; - ; Parameters - ; BC = Length of requested memory block + ; MEM_FREE + ; Frees a block of memory ; -; Returns: - ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) - ; if the block could not be allocated (out of memory) +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done ; --------------------------------------------------------------------- -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified PROC - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - TEMP EQU TEMP0 + 1 + ld a, h + or l + ret z ; Return if NULL pointer - ld hl, 0 - ld (TEMP), hl + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__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/spyder/zxbasic/library-asm/alloc.asm" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) + +__MEM_LOOP2: 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 + inc hl ; Next block ptr - ex de, hl ld e, (hl) inc hl - ld d, (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 - 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) + dec hl + + ld (hl), c 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), b ; (DE) <- BC - ld (hl), e + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) - ex de, hl + 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 (hl), d + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC dec hl - ld (hl), e ; Store new block length + ld c, (hl) ; - add hl, de ; New length + DE => free-block start - pop de ; Remove previous HL off the stack + 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 - ld (hl), c ; Store length on its 1st word +__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 - ld (hl), b - inc hl ; Return hl - ret - - ENDP + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length -#line 7 "inkey.asm" + ld b, h + ld c, l ; BC = Total Length -INKEY: - PROC - LOCAL __EMPTY_INKEY - LOCAL KEY_SCAN - LOCAL KEY_TEST - LOCAL KEY_CODE + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next - ld bc, 3 ; 1 char length string - call __MEM_ALLOC + 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 - ld a, h - or l - ret z ; Return if NULL (No memory) + ENDP - push hl ; Saves memory pointer +#line 3 "asc.asm" - call KEY_SCAN - jp nz, __EMPTY_INKEY +__ASC: + PROC + LOCAL __ASC_END - 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 + ex af, af' ; Saves free_mem flag - ld (hl), 1 - inc hl - ld (hl), 0 + ld a, h + or l + ret z ; NULL? return + + ld c, (hl) inc hl - ld (hl), a - dec hl - dec hl ; HL Points to string result - ret + ld b, (hl) + + ld a, b + or c + jr z, __ASC_END ; No length? return -__EMPTY_INKEY: - pop hl - xor a - ld (hl), a inc hl - ld (hl), a + ld a, (hl) + dec hl + +__ASC_END: dec hl - ret + ex af, af' + or a + call nz, __MEM_FREE ; Free memory if needed - KEY_SCAN EQU 028Eh - KEY_TEST EQU 031Eh - KEY_CODE EQU 0333h + ex af, af' ; Recover result + ret ENDP - #line 22 "codecrash3.bas" -#line 1 "asc.asm" - ; Returns the ascii code for the given str -#line 1 "free.asm" +#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) @@ -502,7 +447,7 @@ __EMPTY_INKEY: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -515,162 +460,217 @@ __EMPTY_INKEY: ; 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 - ; --------------------------------------------------------------------- - ; 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 + ; Error code definitions (as in ZX spectrum manual) - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - ld a, h - or l - ret z ; Return if NULL pointer - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next + ; 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" - 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_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_LINK_PREV: ; Link (DE) with BC, and BC->next with HL - ex de, hl - push hl - dec hl +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC - ld (hl), c - inc hl - ld (hl), b ; (DE) <- BC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - 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 + TEMP EQU TEMP0 + 1 - call __MEM_JOIN_TEST - pop hl + ld hl, 0 + ld (TEMP), hl -__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them - ; hl = Ptr to current block + 2 - ld d, (hl) - dec 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" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ; HL = Pointer to Free block ld e, (hl) - dec hl - ld b, (hl) ; Loads block length into BC - dec hl - ld c, (hl) ; + inc hl + ld d, (hl) + inc hl ; DE = Block Length - 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 + 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 - ret nz + ld (TEMP), hl -__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 + ld e, (hl) 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) + 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 d, (hl) ; DE = block->next + 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 - 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 + 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 3 "asc.asm" -__ASC: - PROC - LOCAL __ASC_END - - ex af, af' ; Saves free_mem flag +#line 7 "inkey.asm" + +INKEY: + PROC + LOCAL __EMPTY_INKEY + LOCAL KEY_SCAN + LOCAL KEY_TEST + LOCAL KEY_CODE + + ld bc, 3 ; 1 char length string + call __MEM_ALLOC ld a, h or l - ret z ; NULL? return + ret z ; Return if NULL (No memory) - ld c, (hl) - inc hl - ld b, (hl) + push hl ; Saves memory pointer - ld a, b - or c - jr z, __ASC_END ; No length? return + 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 a, (hl) - dec hl - -__ASC_END: + ld (hl), 0 + inc hl + ld (hl), a dec hl - ex af, af' - or a - call nz, __MEM_FREE ; Free memory if needed - - ex af, af' ; Recover result + dec hl ; HL Points to string result + ret +__EMPTY_INKEY: + pop hl + xor a + ld (hl), a + inc hl + ld (hl), a + dec hl ret + + KEY_SCAN EQU 028Eh + KEY_TEST EQU 031Eh + KEY_CODE EQU 0333h + ENDP + #line 23 "codecrash3.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/codecrash4.asm b/tests/functional/codecrash4.asm index dcf66c23f..858ecd965 100644 --- a/tests/functional/codecrash4.asm +++ b/tests/functional/codecrash4.asm @@ -43,8 +43,9 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "strcat.asm" -#line 1 "alloc.asm" +#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) @@ -99,7 +100,7 @@ __CALL_BACK__: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -112,47 +113,6 @@ __CALL_BACK__: ; 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_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 @@ -279,264 +239,162 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" - +#line 69 "free.asm" ; --------------------------------------------------------------------- - ; MEM_ALLOC - ; Allocates a block of memory in the heap. - ; - ; Parameters - ; BC = Length of requested memory block + ; MEM_FREE + ; Frees a block of memory ; -; Returns: - ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) - ; if the block could not be allocated (out of memory) +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done ; --------------------------------------------------------------------- -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified PROC - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - TEMP EQU TEMP0 + 1 + ld a, h + or l + ret z ; Return if NULL pointer - ld hl, 0 - ld (TEMP), hl + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__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/spyder/zxbasic/library-asm/alloc.asm" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) + +__MEM_LOOP2: 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 + inc hl ; Next block ptr - ex de, hl ld e, (hl) inc hl - ld d, (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 - 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) + dec hl + + ld (hl), c 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), b ; (DE) <- BC - ld (hl), e + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) - ex de, hl + 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 (hl), d + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC dec hl - ld (hl), e ; Store new block length + ld c, (hl) ; - add hl, de ; New length + DE => free-block start - pop de ; Remove previous HL off the stack + 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 - ld (hl), c ; Store length on its 1st word +__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 - ld (hl), b - inc hl ; Return hl - ret - - ENDP + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length -#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 + ld b, h + ld c, l ; BC = Total Length -__STRLEN: ; Direct FASTCALL entry - ld a, h - or l - ret z + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next - ld a, (hl) - inc hl - ld h, (hl) ; LEN(str) in HL - ld l, a - ret + 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 3 "strcat.asm" +#line 3 "asc.asm" -__ADDSTR: ; Implements c$ = a$ + b$ - ; hl = &a$, de = &b$ (pointers) +__ASC: + PROC + LOCAL __ASC_END + + ex af, af' ; Saves free_mem flag + ld a, h + or l + ret z ; NULL? return -__STRCAT2: ; This routine creates a new string in dynamic space - ; making room for it. Then copies a$ + b$ into it. - ; HL = a$, DE = b$ + ld c, (hl) + inc hl + ld b, (hl) - PROC + ld a, b + or c + jr z, __ASC_END ; No length? return - 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 + inc hl + ld a, (hl) + dec hl + +__ASC_END: + dec hl + ex af, af' + or a + call nz, __MEM_FREE ; Free memory if needed - ENDP + ex af, af' ; Recover result + ret + ENDP #line 32 "codecrash4.bas" #line 1 "inkey.asm" ; INKEY Function @@ -544,65 +402,7 @@ __STRCATEND: ; containing the string. ; An empty string otherwise. - - -INKEY: - PROC - LOCAL __EMPTY_INKEY - LOCAL KEY_SCAN - LOCAL KEY_TEST - LOCAL KEY_CODE - - ld bc, 3 ; 1 char length string - call __MEM_ALLOC - - ld a, h - or l - ret z ; Return if NULL (No memory) - - push hl ; Saves memory pointer - - call KEY_SCAN - jp nz, __EMPTY_INKEY - - call KEY_TEST - jp nc, __EMPTY_INKEY - - dec d ; D is expected to be FLAGS so set bit 3 $FF - ; 'L' Mode so no keywords. - ld e, a ; main key to A - ; C is MODE 0 'KLC' from above still. - call KEY_CODE ; routine K-DECODE - pop hl - - ld (hl), 1 - inc hl - ld (hl), 0 - inc hl - ld (hl), a - dec hl - dec hl ; HL Points to string result - ret - -__EMPTY_INKEY: - pop hl - xor a - ld (hl), a - inc hl - ld (hl), a - dec hl - ret - - KEY_SCAN EQU 028Eh - KEY_TEST EQU 031Eh - KEY_CODE EQU 0333h - - ENDP - -#line 33 "codecrash4.bas" -#line 1 "asc.asm" - ; Returns the ascii code for the given str -#line 1 "free.asm" +#line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -657,7 +457,7 @@ __EMPTY_INKEY: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -670,162 +470,362 @@ __EMPTY_INKEY: ; 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 - ; --------------------------------------------------------------------- - ; 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 + ; Error code definitions (as in ZX spectrum manual) - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - ld a, h - or l - ret z ; Return if NULL pointer - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next + ; 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" - 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_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_LINK_PREV: ; Link (DE) with BC, and BC->next with HL - ex de, hl - push hl - dec hl +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC - ld (hl), c - inc hl - ld (hl), b ; (DE) <- BC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - 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 + TEMP EQU TEMP0 + 1 - call __MEM_JOIN_TEST - pop hl + ld hl, 0 + ld (TEMP), hl -__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them - ; hl = Ptr to current block + 2 - ld d, (hl) - dec 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" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ; HL = Pointer to Free block ld e, (hl) - dec hl - ld b, (hl) ; Loads block length into BC - dec hl - ld c, (hl) ; + inc hl + ld d, (hl) + inc hl ; DE = Block Length - 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 + 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 - ret nz + ld (TEMP), hl -__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 + ld e, (hl) 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) + 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 d, (hl) ; DE = block->next + 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 - 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 + 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 3 "asc.asm" -__ASC: - PROC - LOCAL __ASC_END - - ex af, af' ; Saves free_mem flag +#line 7 "inkey.asm" + +INKEY: + PROC + LOCAL __EMPTY_INKEY + LOCAL KEY_SCAN + LOCAL KEY_TEST + LOCAL KEY_CODE + + ld bc, 3 ; 1 char length string + call __MEM_ALLOC ld a, h or l - ret z ; NULL? return + ret z ; Return if NULL (No memory) - ld c, (hl) - inc hl - ld b, (hl) + push hl ; Saves memory pointer - ld a, b - or c - jr z, __ASC_END ; No length? return + 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 a, (hl) - dec hl - -__ASC_END: + ld (hl), 0 + inc hl + ld (hl), a dec hl - ex af, af' - or a - call nz, __MEM_FREE ; Free memory if needed - - ex af, af' ; Recover result + dec hl ; HL Points to string result + ret +__EMPTY_INKEY: + pop hl + xor a + ld (hl), a + inc hl + ld (hl), a + dec hl ret + + KEY_SCAN EQU 028Eh + KEY_TEST EQU 031Eh + KEY_CODE EQU 0333h + ENDP + +#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$ + + 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 34 "codecrash4.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/coercion1.asm b/tests/functional/coercion1.asm index b6f250cb1..6c19f1b5d 100644 --- a/tests/functional/coercion1.asm +++ b/tests/functional/coercion1.asm @@ -136,82 +136,15 @@ __ADDF: ; Addition jp __FPSTACK_POP #line 59 "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. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- +#line 1 "border.asm" + ; __FASTCALL__ Routine to change de border + ; Parameter (color) specified in A register -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC + BORDER EQU 229Bh - jp __FPSTACK_POP + ; Nothing to do! (Directly from the ZX Spectrum ROM) #line 60 "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 - ;; 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 61 "coercion1.bas" #line 1 "divf.asm" #line 1 "error.asm" @@ -313,16 +246,7 @@ __DIVBYZERO: ENDP -#line 62 "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 63 "coercion1.bas" +#line 61 "coercion1.bas" #line 1 "ftou32reg.asm" #line 1 "neg32.asm" __ABS32: @@ -431,6 +355,82 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A 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 + ;; 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 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. + ; 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 64 "coercion1.bas" #line 1 "pushf.asm" diff --git a/tests/functional/coercion3.asm b/tests/functional/coercion3.asm index 0f4dad958..c438448b8 100644 --- a/tests/functional/coercion3.asm +++ b/tests/functional/coercion3.asm @@ -38,9 +38,8 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "paper.asm" - ; Sets paper color in ATTR_P permanently -; Parameter: Paper color in A register +#line 1 "copy_attr.asm" +#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" #line 1 "const.asm" ; Global constants @@ -53,7 +52,51 @@ __CALL_BACK__: UDG EQU 23675 ; Pointer to UDG Charset MEM0 EQU 5C92h ; Temporary memory buffer used by ROM chars -#line 5 "paper.asm" +#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 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + ret +#line 65 "/Users/boriel/Documents/src/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 30 "coercion3.bas" +#line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + + PAPER: PROC @@ -96,49 +139,6 @@ PAPER_TMP: jp __SET_PAPER ENDP -#line 30 "coercion3.bas" -#line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/spyder/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) - -#line 63 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/copy_attr.asm" - ret -#line 65 "/Users/boriel/Documents/src/spyder/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 31 "coercion3.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/draw3.asm b/tests/functional/draw3.asm index f87047ca0..66fbfff69 100644 --- a/tests/functional/draw3.asm +++ b/tests/functional/draw3.asm @@ -84,115 +84,6 @@ __END_PROGRAM: ret __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 - - 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, __NEG32 ; Negates DEHL - - ret - - ENDP - - -__FTOU8: ; Converts float in C ED LH to Unsigned byte in A - call __FTOU32REG - ld a, l - ret - -#line 76 "draw3.bas" #line 1 "draw3.asm" ; ----------------------------------------------------------- ; vim: et:ts=4:sw=4:ruler: @@ -1477,6 +1368,115 @@ SUM_B: 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 + + 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, __NEG32 ; Negates DEHL + + ret + + 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: diff --git a/tests/functional/einarattr.asm b/tests/functional/einarattr.asm index c72a5cece..1339d1f55 100644 --- a/tests/functional/einarattr.asm +++ b/tests/functional/einarattr.asm @@ -50,72 +50,14 @@ _printA__leave: ld sp, ix pop ix ret -__LABEL1: - DEFW 0001h - DEFB 41h __LABEL0: DEFW 0001h DEFB 42h -#line 1 "paper.asm" - ; Sets paper 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 "paper.asm" - -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 +__LABEL1: + DEFW 0001h + DEFB 41h +#line 1 "copy_attr.asm" -#line 44 "einarattr.bas" -#line 1 "printstr.asm" #line 1 "print.asm" ; vim:ts=4:sw=4:et: ; PRINT command routine @@ -297,7 +239,18 @@ CALL_HL: ; 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 @@ -338,7 +291,54 @@ INK_TMP: 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 @@ -398,82 +398,7 @@ BRIGHT_TMP: #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" - - - -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/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: @@ -1163,7 +1088,84 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#line 3 "copy_attr.asm" +#line 4 "/home/boriel/Documents/src/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 "/home/boriel/Documents/src/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 44 "einarattr.bas" + + +#line 1 "printstr.asm" + #line 1 "free.asm" @@ -1535,9 +1537,7 @@ __PRINT_STR: ENDP -#line 45 "einarattr.bas" - - +#line 47 "einarattr.bas" ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: diff --git a/tests/functional/einarshift.asm b/tests/functional/einarshift.asm index 25bc5bf2a..329aabbe1 100644 --- a/tests/functional/einarshift.asm +++ b/tests/functional/einarshift.asm @@ -41,9 +41,6 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "printu8.asm" -#line 1 "printi8.asm" -#line 1 "printnum.asm" #line 1 "print.asm" ; vim:ts=4:sw=4:et: ; PRINT command routine @@ -1149,7 +1146,11 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printnum.asm" +#line 32 "einarshift.bas" +#line 1 "printu8.asm" +#line 1 "printi8.asm" +#line 1 "printnum.asm" + __PRINTU_START: @@ -1311,8 +1312,7 @@ __PRINTU_LOOP: #line 2 "printu8.asm" -#line 32 "einarshift.bas" - +#line 33 "einarshift.bas" ZXBASIC_USER_DATA: _a: diff --git a/tests/functional/emptystrparam.asm b/tests/functional/emptystrparam.asm index d37488425..9acb6cee4 100644 --- a/tests/functional/emptystrparam.asm +++ b/tests/functional/emptystrparam.asm @@ -519,9 +519,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/fastcall0.asm b/tests/functional/fastcall0.asm index af30b7127..325f8acfb 100644 --- a/tests/functional/fastcall0.asm +++ b/tests/functional/fastcall0.asm @@ -480,95 +480,6 @@ _SPFill__leave: __LABEL0: DEFW 0001h DEFB 61h -#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 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 470 "fastcall0.bas" #line 1 "circle.asm" ; Bresenham's like circle algorithm ; best known as Middle Point Circle drawing algorithm @@ -623,7 +534,39 @@ __STOP: #line 1 "in_screen.asm" +#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 2 "in_screen.asm" __IN_SCREEN: @@ -653,8 +596,56 @@ __OUT_OF_SCREEN_ERR: 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 + 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 10 "plot.asm" + PLOT: PROC @@ -943,6 +934,15 @@ __CIRCLE_PLOT: 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. diff --git a/tests/functional/for0.asm b/tests/functional/for0.asm index a5f070741..8809e9778 100644 --- a/tests/functional/for0.asm +++ b/tests/functional/for0.asm @@ -135,7 +135,32 @@ __CLS_SCR: ENDP #line 42 "for0.bas" -#line 1 "printstr.asm" +#line 1 "lti8.asm" + +__LTI8: ; Test 8 bit values A < H + ; Returns result in A: 0 = False, !0 = True + sub h + +__LTI: ; Signed CMP + PROC + LOCAL __PE + + ld a, 0 ; Sets default to false +__LTI2: + jp pe, __PE + ; Overflow flag NOT set + ret p + dec a ; TRUE + +__PE: ; Overflow set + ret m + dec a ; TRUE + ret + + ENDP +#line 43 "for0.bas" +#line 1 "printi8.asm" +#line 1 "printnum.asm" #line 1 "print.asm" ; vim:ts=4:sw=4:et: ; PRINT command routine @@ -1161,7 +1186,169 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#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 + 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 44 "for0.bas" +#line 1 "printstr.asm" + #line 1 "free.asm" @@ -1533,193 +1720,6 @@ __PRINT_STR: ENDP -#line 43 "for0.bas" -#line 1 "lti8.asm" - -__LTI8: ; Test 8 bit values A < H - ; Returns result in A: 0 = False, !0 = True - sub h - -__LTI: ; Signed CMP - PROC - LOCAL __PE - - ld a, 0 ; Sets default to false -__LTI2: - jp pe, __PE - ; Overflow flag NOT set - ret p - dec a ; TRUE - -__PE: ; Overflow set - ret m - dec a ; TRUE - ret - - ENDP -#line 44 "for0.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 45 "for0.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/ifelse1.asm b/tests/functional/ifelse1.asm index 8f05b8bd5..e49c3da2f 100644 --- a/tests/functional/ifelse1.asm +++ b/tests/functional/ifelse1.asm @@ -56,7 +56,6 @@ __LABEL2: DEFB 65h DEFB 6Eh DEFB 21h -#line 1 "printstr.asm" #line 1 "print.asm" ; vim:ts=4:sw=4:et: ; PRINT command routine @@ -1162,7 +1161,9 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#line 44 "ifelse1.bas" +#line 1 "printstr.asm" + #line 1 "free.asm" @@ -1534,8 +1535,7 @@ __PRINT_STR: ENDP -#line 44 "ifelse1.bas" - +#line 45 "ifelse1.bas" ZXBASIC_USER_DATA: _a: diff --git a/tests/functional/inkey.asm b/tests/functional/inkey.asm index ae1345cc5..1f8e5c285 100644 --- a/tests/functional/inkey.asm +++ b/tests/functional/inkey.asm @@ -42,15 +42,13 @@ __END_PROGRAM: ret __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 "inkey.asm" + ; INKEY Function + ; Returns a string allocated in dynamic memory + ; containing the string. + ; An empty string otherwise. -#line 1 "free.asm" +#line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -105,7 +103,7 @@ __CALL_BACK__: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -118,6 +116,47 @@ __CALL_BACK__: ; 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_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 @@ -244,162 +283,177 @@ __MEM_INIT2: ENDP -#line 69 "free.asm" +#line 70 "alloc.asm" + ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory + ; MEM_ALLOC + ; Allocates a block of memory in the heap. ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done + ; 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_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) PROC - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - ld a, h - or l - ret z ; Return if NULL pointer + TEMP EQU TEMP0 + 1 - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + ld hl, 0 + ld (TEMP), hl +__MEM_START: ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start - -__MEM_LOOP2: + 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" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) inc hl - inc hl ; Next block ptr + 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) ; 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 + 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 - dec hl - - ld (hl), c + 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 (hl), b ; (DE) <- BC + 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 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 + ld (hl), e inc hl - ld (hl), d - ; --- LINKED ; HL = &(BC->next) + 2 - - call __MEM_JOIN_TEST - pop hl + ld (hl), d ; LINKED + pop hl ; Returning block. + + ret -__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them - ; hl = Ptr to current block + 2 - ld d, (hl) +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl dec hl - ld e, (hl) - dec hl - ld b, (hl) ; Loads block length into BC + ld (hl), d dec hl - ld c, (hl) ; + ld (hl), e ; Store new block length - 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 + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack -__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) + ld (hl), c ; Store length on its 1st word inc hl + ld (hl), b + inc hl ; Return hl + ret + + ENDP - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length - ld b, h - ld c, l ; BC = Total Length +#line 7 "inkey.asm" - ex de, hl - ld e, (hl) - inc hl - ld d, (hl) ; DE = block->next +INKEY: + PROC + LOCAL __EMPTY_INKEY + LOCAL KEY_SCAN + LOCAL KEY_TEST + LOCAL KEY_CODE - 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 + ld bc, 3 ; 1 char length string + call __MEM_ALLOC - ENDP + ld a, h + or l + ret z ; Return if NULL (No memory) -#line 9 "storestr2.asm" + push hl ; Saves memory pointer -__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) - push ix - pop hl - add hl, bc + call KEY_SCAN + jp nz, __EMPTY_INKEY + + call KEY_TEST + jp nc, __EMPTY_INKEY -__ISTORE_STR2: - ld c, (hl) ; Dereferences HL - inc hl - ld h, (hl) - ld l, c ; HL = *HL (real string variable address) + 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 -__STORE_STR2: - push hl - ld c, (hl) + ld (hl), 1 inc hl - ld h, (hl) - ld l, c ; HL = *HL (real string address) - - push de - call __MEM_FREE - pop de + ld (hl), 0 + inc hl + ld (hl), a + dec hl + dec hl ; HL Points to string result + ret +__EMPTY_INKEY: pop hl - ld (hl), e + xor a + ld (hl), a inc hl - ld (hl), d - dec hl ; HL points to mem address variable. This might be useful in the future. - + 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 "printstr.asm" #line 1 "print.asm" ; vim:ts=4:sw=4:et: ; PRINT command routine @@ -489,48 +543,8 @@ __CLS_SCR: #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_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) @@ -1505,71 +1519,23 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "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 +#line 31 "inkey.bas" +#line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves + ; 3 bytes - 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 +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 32 "inkey.bas" +#line 1 "printstr.asm" -#line 31 "inkey.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" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -1624,7 +1590,7 @@ __PRINT_STR: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -1639,186 +1605,220 @@ __PRINT_STR: - - ; --------------------------------------------------------------------- - ; MEM_ALLOC - ; Allocates a block of memory in the heap. - ; - ; Parameters - ; BC = Length of requested memory block + ; MEM_FREE + ; Frees a block of memory ; -; Returns: - ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) - ; if the block could not be allocated (out of memory) +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done ; --------------------------------------------------------------------- -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified PROC - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - TEMP EQU TEMP0 + 1 + ld a, h + or l + ret z ; Return if NULL pointer - ld hl, 0 - ld (TEMP), hl + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__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" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) + +__MEM_LOOP2: 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 + inc hl ; Next block ptr - ex de, hl ld e, (hl) inc hl - ld d, (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 - 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) + dec hl + + ld (hl), c 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), b ; (DE) <- BC - ld (hl), e + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) - ex de, hl + 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 (hl), d + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC dec hl - ld (hl), e ; Store new block length + ld c, (hl) ; - add hl, de ; New length + DE => free-block start - pop de ; Remove previous HL off the stack + 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 - ld (hl), c ; Store length on its 1st word +__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 (hl), b - inc hl ; Return 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" -#line 7 "inkey.asm" + ; PRINT command routine + ; Prints string pointed by HL -INKEY: - PROC - LOCAL __EMPTY_INKEY - LOCAL KEY_SCAN - LOCAL KEY_TEST - LOCAL KEY_CODE +PRINT_STR: +__PRINTSTR: ; __FASTCALL__ Entry to print_string + PROC + LOCAL __PRINT_STR_LOOP + LOCAL __PRINT_STR_END - ld bc, 3 ; 1 char length string - call __MEM_ALLOC + ld d, a ; Saves A reg (Flag) for later - ld a, h - or l - ret z ; Return if NULL (No memory) + ld a, h + or l + ret z ; Return if the pointer is NULL - push hl ; Saves memory pointer + push hl - call KEY_SCAN - jp nz, __EMPTY_INKEY - - call KEY_TEST - jp nc, __EMPTY_INKEY + ld c, (hl) + inc hl + ld b, (hl) + inc hl ; BC = LEN(a$); HL = &a$ - 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 +__PRINT_STR_LOOP: + ld a, b + or c + jr z, __PRINT_STR_END ; END if BC (counter = 0) - ld (hl), 1 - inc hl - ld (hl), 0 - inc hl - ld (hl), a - dec hl - dec hl ; HL Points to string result - ret + ld a, (hl) + call __PRINTCHAR + inc hl + dec bc + jp __PRINT_STR_LOOP -__EMPTY_INKEY: - pop hl - xor a - ld (hl), a - inc hl - ld (hl), a - dec hl - ret +__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 - KEY_SCAN EQU 028Eh - KEY_TEST EQU 031Eh - KEY_CODE EQU 0333h +__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 + ENDP #line 33 "inkey.bas" -#line 1 "print_eol_attr.asm" - ; Calls PRINT_EOL and then COPY_ATTR, so saves - ; 3 bytes +#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 -PRINT_EOL_ATTR: - call PRINT_EOL - jp COPY_ATTR #line 34 "inkey.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/inktemp.asm b/tests/functional/inktemp.asm index 7946437ca..897d617ab 100644 --- a/tests/functional/inktemp.asm +++ b/tests/functional/inktemp.asm @@ -51,13 +51,9 @@ __END_PROGRAM: ret __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 "circle.asm" + ; Bresenham's like circle algorithm + ; best known as Middle Point Circle drawing algorithm #line 1 "error.asm" ; Simple error control routines @@ -99,7 +95,15 @@ __ERROR_CODE: __STOP: ld (ERR_NR), a ret -#line 9 "draw.asm" +#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. @@ -162,16 +166,7 @@ __OUT_OF_SCREEN_ERR: jp __STOP ; Saves error code and exits ENDP -#line 10 "draw.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 9 "plot.asm" #line 1 "cls.asm" ; JUMPS directly to spectrum CLS ; This routine does not clear lower screen @@ -301,212 +296,486 @@ __PLOT_ERR: PIXEL_ADDR EQU 22ACh COORDS EQU 5C7Dh ENDP -#line 11 "draw.asm" +#line 6 "circle.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 -#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 -#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 - -#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 - -#line 17 "draw.asm" + ; Draws a circle at X, Y of radius R + ; X, Y on the Stack, R in accumulator (Byte) - ;; DRAW PROCEDURE - PROC + PROC + LOCAL __CIRCLE_ERROR + LOCAL __CIRCLE_LOOP + LOCAL __CIRCLE_NEXT - LOCAL __DRAW1 - LOCAL __DRAW2 - LOCAL __DRAW3 - LOCAL __DRAW4, __DRAW4_LOOP - LOCAL __DRAW5 - LOCAL __DRAW6, __DRAW6_LOOP - LOCAL __DRAW_ERROR - LOCAL DX1, DX2, DY1, DY2 - LOCAL __INCX, __INCY, __DECX, __DECY - LOCAL P_FLAG - P_FLAG EQU 23697 +__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 +CIRCLE: + ;; Entry point + pop hl ; Return Address + pop de ; D = Y + ex (sp), hl ; __CALLEE__ convention + ld e, h ; E = X -__DRAW_ERROR: - jp __OUT_OF_SCREEN_ERR -DRAW: - ;; ENTRY POINT + ld h, a ; H = R + add a, d + sub 192 + jr nc, __CIRCLE_ERROR - LOCAL PIXEL_ADDR - LOCAL COORDS - LOCAL __DRAW_SETUP1, __DRAW_START, __PLOTOVER, __PLOTINVERSE + ld a, d + sub h + jr c, __CIRCLE_ERROR - ex de, hl ; DE = Y OFFSET - pop hl ; return addr - ex (sp), hl ; CALLEE => HL = X OFFSET - ld bc, (COORDS) + ld a, e + sub h + jr c, __CIRCLE_ERROR - ld a, c - add a, l - ld l, a - ld a, h - adc a, 0 ; HL = HL + C - ld h, a - jr nz, __DRAW_ERROR ; if a <> 0 => Out of Screen + ld a, h + add a, e + jr c, __CIRCLE_ERROR - ld a, b - add a, e - ld e, a - ld a, d - adc a, 0 ; DE = DE + B - ld d, a - jr nz, __DRAW_ERROR ; if a <> 0 => Out of Screen - ld a, 191 - sub e +; __FASTCALL__ Entry: D, E = Y, X point of the center + ; A = Radious +__CIRCLE: + push de + ld a, h + exx + pop de ; D'E' = x0, y0 + ld h, a ; H' = r + + ld c, e + ld a, h + add a, d + ld b, a + call __CIRCLE_PLOT ; PLOT (x0, y0 + r) + + ld b, d + ld a, h + add a, e + ld c, a + call __CIRCLE_PLOT ; PLOT (x0 + r, y0) + + ld c, e + ld a, d + sub h + ld b, a + call __CIRCLE_PLOT ; PLOT (x0, y0 - r) + + ld b, d + ld a, e + sub h + ld c, a + call __CIRCLE_PLOT ; PLOT (x0 - r, y0) + + exx + ld b, 0 ; B = x = 0 + ld c, h ; C = y = Radius + ld hl, 1 + or a + sbc hl, bc ; HL = f = 1 - radius + + ex de, hl + ld hl, 0 + or a + sbc hl, bc ; HL = -radius + add hl, hl ; HL = -2 * radius + ex de, hl ; DE = -2 * radius = ddF_y, HL = f + + xor a ; A = ddF_x = 0 + ex af, af' ; Saves it + +__CIRCLE_LOOP: + ld a, b + cp c + ret nc ; Returns when x >= y + + bit 7, h ; HL >= 0? : if (f >= 0)... + jp nz, __CIRCLE_NEXT + + dec c ; y-- + inc de + inc de ; ddF_y += 2 + + add hl, de ; f += ddF_y + +__CIRCLE_NEXT: + inc b ; x++ + ex af, af' + add a, 2 ; 1 Cycle faster than inc a, inc a + + inc hl ; f++ + push af + add a, l + ld l, a + ld a, h + adc a, 0 ; f = f + ddF_x + ld h, a + pop af + ex af, af' + + push bc + exx + pop hl ; H'L' = Y, X + + ld a, d + add a, h + ld b, a ; B = y0 + y + ld a, e + add a, l + ld c, a ; C = x0 + x + call __CIRCLE_PLOT ; plot(x0 + x, y0 + y) + + ld a, d + add a, h + ld b, a ; B = y0 + y + ld a, e + sub l + ld c, a ; C = x0 - x + call __CIRCLE_PLOT ; plot(x0 - x, y0 + y) + + ld a, d + sub h + ld b, a ; B = y0 - y + ld a, e + add a, l + ld c, a ; C = x0 + x + call __CIRCLE_PLOT ; plot(x0 + x, y0 - y) + + ld a, d + sub h + ld b, a ; B = y0 - y + ld a, e + sub l + ld c, a ; C = x0 - x + call __CIRCLE_PLOT ; plot(x0 - x, y0 - y) + + ld a, d + add a, l + ld b, a ; B = y0 + x + ld a, e + add a, h + ld c, a ; C = x0 + y + call __CIRCLE_PLOT ; plot(x0 + y, y0 + x) + + ld a, d + add a, l + ld b, a ; B = y0 + x + ld a, e + sub h + ld c, a ; C = x0 - y + call __CIRCLE_PLOT ; plot(x0 - y, y0 + x) + + ld a, d + sub l + ld b, a ; B = y0 - x + ld a, e + add a, h + ld c, a ; C = x0 + y + call __CIRCLE_PLOT ; plot(x0 + y, y0 - x) + + ld a, d + sub l + ld b, a ; B = y0 - x + ld a, e + sub h + ld c, a ; C = x0 + y + call __CIRCLE_PLOT ; plot(x0 - y, y0 - x) + + exx + jp __CIRCLE_LOOP + + + +__CIRCLE_PLOT: + ; Plots a point of the circle, preserving HL and DE + push hl + push de + call __PLOT + pop de + pop hl + ret + + ENDP +#line 43 "inktemp.bas" +#line 1 "copy_attr.asm" +#line 4 "/Users/boriel/Documents/src/zxbasic/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 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + ret +#line 65 "/Users/boriel/Documents/src/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 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 +#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 +#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 + +#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 + +#line 17 "draw.asm" + + ;; DRAW PROCEDURE + PROC + + LOCAL __DRAW1 + LOCAL __DRAW2 + LOCAL __DRAW3 + LOCAL __DRAW4, __DRAW4_LOOP + LOCAL __DRAW5 + LOCAL __DRAW6, __DRAW6_LOOP + LOCAL __DRAW_ERROR + LOCAL DX1, DX2, DY1, DY2 + LOCAL __INCX, __INCY, __DECX, __DECY + LOCAL P_FLAG + P_FLAG EQU 23697 + +__DRAW_ERROR: + jp __OUT_OF_SCREEN_ERR + +DRAW: + ;; ENTRY POINT + + LOCAL 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 + ld a, h + adc a, 0 ; HL = HL + C + ld h, a + jr nz, __DRAW_ERROR ; if a <> 0 => Out of Screen + + ld a, b + add a, e + ld e, a + ld a, d + adc a, 0 ; DE = DE + B + ld d, a + jr nz, __DRAW_ERROR ; if a <> 0 => Out of Screen + + ld a, 191 + sub e jr c, __DRAW_ERROR ; Out of screen ld h, e ; now H,L = y2, x2 @@ -784,200 +1053,7 @@ __FASTPLOTEND: ENDP -#line 43 "inktemp.bas" -#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 44 "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 - 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 45 "inktemp.bas" -#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/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) - -#line 63 "/Users/boriel/Documents/src/zxb/trunk/library-asm/copy_attr.asm" - ret -#line 65 "/Users/boriel/Documents/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 46 "inktemp.bas" #line 1 "flash.asm" ; Sets flash flag in ATTR_P permanently ; Parameter: Paper color in A register @@ -1004,224 +1080,148 @@ FLASH_TMP: ld de, ATTR_T jr __SET_FLASH -#line 47 "inktemp.bas" - - -#line 1 "circle.asm" - ; Bresenham's like circle algorithm - ; best known as Middle Point Circle drawing algorithm - - - - - - ; 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 -CIRCLE: - ;; Entry point - pop hl ; Return Address - pop de ; D = Y - ex (sp), hl ; __CALLEE__ convention - ld e, h ; E = X - - - ld h, a ; H = R - add a, d - sub 192 - jr nc, __CIRCLE_ERROR +#line 46 "inktemp.bas" +#line 1 "ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register - 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 +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P -; __FASTCALL__ Entry: D, E = Y, X point of the center - ; A = Radious -__CIRCLE: - push de - ld a, h - exx - pop de ; D'E' = x0, y0 - ld h, a ; H' = r +__SET_INK: + cp 8 + jr nz, __SET_INK2 - ld c, e - ld a, h - add a, d - ld b, a - call __CIRCLE_PLOT ; PLOT (x0, y0 + r) + 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 - ld b, d - ld a, h - add a, e - ld c, a - call __CIRCLE_PLOT ; PLOT (x0 + r, y0) +__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 - ld c, e - ld a, d - sub h - ld b, a - call __CIRCLE_PLOT ; PLOT (x0, y0 - r) + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK - ld b, d - ld a, e - sub h - ld c, a - call __CIRCLE_PLOT ; PLOT (x0 - r, y0) + ENDP - 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 +#line 47 "inktemp.bas" +#line 1 "over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register - 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 +OVER: + PROC - bit 7, h ; HL >= 0? : if (f >= 0)... - jp nz, __CIRCLE_NEXT + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a - dec c ; y-- - inc de - inc de ; ddF_y += 2 + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent - add hl, de ; f += ddF_y + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret -__CIRCLE_NEXT: - inc b ; x++ - ex af, af' - add a, 2 ; 1 Cycle faster than inc a, inc a + ; 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 - inc hl ; f++ - push af - add a, l - ld l, a - ld a, h - adc a, 0 ; f = f + ddF_x - ld h, a - pop af - ex af, af' + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE - push bc - exx - pop hl ; H'L' = Y, X - - ld a, d - add a, h - ld b, a ; B = y0 + y - ld a, e - add a, l - ld c, a ; C = x0 + x - call __CIRCLE_PLOT ; plot(x0 + x, y0 + y) + ENDP - ld a, d - add a, h - ld b, a ; B = y0 + y - ld a, e - sub l - ld c, a ; C = x0 - x - call __CIRCLE_PLOT ; plot(x0 - x, y0 + y) +#line 48 "inktemp.bas" +#line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register - ld a, d - sub h - ld b, a ; B = y0 - y - ld a, e - add a, l - ld c, a ; C = x0 + x - call __CIRCLE_PLOT ; plot(x0 + x, y0 - y) - ld a, d - sub h - ld b, a ; B = y0 - y - ld a, e - sub l - ld c, a ; C = x0 - x - call __CIRCLE_PLOT ; plot(x0 - x, y0 - y) - - ld a, d - add a, l - ld b, a ; B = y0 + x - ld a, e - add a, h - ld c, a ; C = x0 + y - call __CIRCLE_PLOT ; plot(x0 + y, y0 + x) - - ld a, d - add a, l - ld b, a ; B = y0 + x - ld a, e - sub h - ld c, a ; C = x0 - y - call __CIRCLE_PLOT ; plot(x0 - y, y0 + x) - ld a, d - sub l - ld b, a ; B = y0 - x - ld a, e - add a, h - ld c, a ; C = x0 + y - call __CIRCLE_PLOT ; plot(x0 + y, y0 - x) +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + + ld de, ATTR_P - ld a, d - sub l - ld b, a ; B = y0 - x - ld a, e - sub h - ld c, a ; C = x0 + y - call __CIRCLE_PLOT ; plot(x0 - y, y0 - x) +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret - exx - jp __CIRCLE_LOOP + ; 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 -__CIRCLE_PLOT: - ; Plots a point of the circle, preserving HL and DE - push hl - push de - call __PLOT - pop de - pop hl - ret - - ENDP -#line 50 "inktemp.bas" + ; 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 diff --git a/tests/functional/lcd3.asm b/tests/functional/lcd3.asm index f63c2d9b9..f6ffc5344 100644 --- a/tests/functional/lcd3.asm +++ b/tests/functional/lcd3.asm @@ -146,19 +146,87 @@ _lset__leave: ex (sp), hl exx ret -__LABEL1: - DEFW 0001h - DEFB 30h __LABEL0: DEFW 0002h DEFB 48h DEFB 50h +__LABEL1: + DEFW 0001h + DEFB 30h __LABEL2: DEFW 0002h DEFB 4Fh DEFB 46h -#line 1 "strcat.asm" -#line 1 "alloc.asm" +#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 145 "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) @@ -213,7 +281,7 @@ __LABEL2: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -226,47 +294,6 @@ __LABEL2: ; 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_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 @@ -393,387 +420,629 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" - +#line 69 "free.asm" ; --------------------------------------------------------------------- - ; MEM_ALLOC - ; Allocates a block of memory in the heap. - ; - ; Parameters - ; BC = Length of requested memory block + ; MEM_FREE + ; Frees a block of memory ; -; Returns: - ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) - ; if the block could not be allocated (out of memory) +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done ; --------------------------------------------------------------------- -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified PROC - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - TEMP EQU TEMP0 + 1 + ld a, h + or l + ret z ; Return if NULL pointer - ld hl, 0 - ld (TEMP), hl + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__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" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) + +__MEM_LOOP2: 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 + inc hl ; Next block ptr - ex de, hl ld e, (hl) inc hl - ld d, (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 - 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) + dec hl + + ld (hl), c 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), b ; (DE) <- BC - ld (hl), e + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) - ex de, hl + 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 (hl), d + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC dec hl - ld (hl), e ; Store new block length + ld c, (hl) ; - add hl, de ; New length + DE => free-block start - pop de ; Remove previous HL off the stack + 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 - ld (hl), c ; Store length on its 1st word +__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 - 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" + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length -__ADDSTR: ; Implements c$ = a$ + b$ - ; hl = &a$, de = &b$ (pointers) + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next -__STRCAT2: ; This routine creates a new string in dynamic space - ; making room for it. Then copies a$ + b$ into it. - ; HL = a$, DE = b$ + 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 - PROC + ENDP - LOCAL __STR_CONT - LOCAL __STRCATEND +#line 146 "lcd3.bas" +#line 1 "ftou32reg.asm" +#line 1 "neg32.asm" +__ABS32: + bit 7, d + ret z - 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$ +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a - inc bc - inc bc ; +2 bytes to store length + ld a, h + cpl + ld h, a - ex de, hl - push hl - call __STRLEN - ; HL = len(b$) + ld a, e + cpl + ld e, a + + ld a, d + cpl + ld d, a - add hl, bc ; Total str length => 2 + len(a$) + len(b$) + inc l + ret nz - ld c, l - ld b, h ; BC = Total str length + 2 - call __MEM_ALLOC - pop de ; HL = c$, DE = b$ + inc h + ret nz - ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ + inc de + ret - exx - pop de ; D'E' = b$ - exx +#line 2 "ftou32reg.asm" - pop bc ; LEN(a$) +__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 - ld a, d - or e - ret z ; If no memory: RETURN + LOCAL __IS_FLOAT -__STR_CONT: - push de ; Address of c$ + or a + jr nz, __IS_FLOAT + ; Here if it is a ZX ROM Integer - ld a, h - or l - jr nz, __STR_CONT1 ; If len(a$) != 0 do copy + 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 - ; 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 +__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) -__STR_CONT1: ; Copies a$ (HL) into c$ (DE) - inc bc - inc bc ; BC = BC + 2 - ldir ; MEMCOPY: c$ = a$ - pop hl ; HL = c$ + push de + push bc - exx - push de ; Recovers b$; A ex hl,hl' would be very handy - exx + exx + pop de ; Loads mantissa into C'B' E'D' + pop bc ; - pop de ; DE = b$ + set 7, c ; Highest mantissa bit is always 1 + exx -__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) + ld hl, 0 ; DEHL = 0 + ld d, h + ld e, l - push hl ; Saves HL to return it later + ;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 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 + ld b, a ; Loop counter = exponent - 128 - ex de, hl ; DE = end of string (Begin of copy addr) - ld c, (hl) - inc hl - ld b, (hl) ; BC = len(b$) +__FTOU32REG_LOOP: + exx ; Shift C'B' E'D' << 1, output bit stays in Carry + sla d + rl e + rl b + rl c - ld a, b - or c - jr z, __STRCATEND; Return if len(b$) == 0 + exx ; Shift DEHL << 1, inserting the carry on the right + rl l + rl h + rl e + rl d - push bc ; Save LEN(b$) - inc hl ; Skip 2nd byte of len(b$) - ldir ; Concatenate b$ + djnz __FTOU32REG_LOOP - 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 +__FTOU32REG_END: + pop af ; Take the sign bit + or a ; Sets SGN bit to 1 if negative + jp m, __NEG32 ; Negates DEHL + + ret - ld (hl), e ; Updates new LEN and return - inc hl - ld (hl), d - dec hl - ret + ENDP -__STRCATEND: - pop hl ; Removes Len(a$) - pop hl ; Restores original HL, so HL = a$ - ret - ENDP +__FTOU8: ; Converts float in C ED LH to Unsigned byte in A + call __FTOU32REG + ld a, l + ret -#line 145 "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 147 "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) +; 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 -#line 1 "sposn.asm" - ; Printing positioning library. - PROC - LOCAL ECHO_E + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. -__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 +; 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)| + ; +----------------+ - ECHO_E EQU 23682 - MAXX EQU ECHO_E ; Max X position + 1 - MAXY EQU MAXX + 1 ; Max Y position + 1 + ; When 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). - S_POSN EQU 23688 - POSX EQU S_POSN ; Current POS X - POSY EQU S_POSN + 1 ; Current POS Y - ENDP + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. -#line 6 "print.asm" -#line 1 "cls.asm" - ; JUMPS directly to spectrum CLS - ; This routine does not clear lower screen + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - ;CLS EQU 0DAFh +#line 1 "error.asm" + ; Simple error control routines +; vim:ts=4:et: - ; Our faster implementation + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) -CLS: - PROC +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - 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 + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - ; Now clear attributes - ld a, (ATTR_P) - ld (hl), a - ld bc, 767 - ldir - ret + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + 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 + ; 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" - 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" + ; --------------------------------------------------------------------- + ; 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 -__IN_SCREEN: - ; Returns NO carry if current coords (D, E) - ; are OUT of the screen limits (MAXX, MAXY) + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - PROC - LOCAL __IN_SCREEN_ERR + TEMP EQU TEMP0 + 1 - ld hl, MAXX - ld a, e - cp (hl) - jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld hl, 0 + ld (TEMP), hl - 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) +__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" + ret z ; NULL +#line 113 "/home/boriel/Documents/src/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 -__IN_SCREEN_ERR: -__OUT_OF_SCREEN_ERR: - ; Jumps here if out of screen - ld a, ERROR_OutOfScreen - jp __STOP ; Saves error code and exits + 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 - ENDP -#line 8 "print.asm" -#line 1 "table_jump.asm" + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + + ret -JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A - add a, a +__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 148 "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 + +__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 @@ -955,7 +1224,7 @@ BRIGHT_TMP: #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" +#line 4 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" @@ -1014,7 +1283,7 @@ TABLE: and (hl) ; OVER 2 MODE or (hl) ; OVER 3 MODE -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" +#line 65 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" __REFRESH_TMP: ld a, (hl) @@ -1148,1041 +1417,581 @@ ITALIC: 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 + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret - ENDP - + ; 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 -#line 146 "lcd3.bas" -#line 1 "str.asm" - ; The STR$( ) BASIC function implementation + ENDP - ; Given a FP number in C ED LH - ; Returns a pointer (in HL) to the memory heap - ; containing the FP number string representation +#line 17 "print.asm" +#line 1 "attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: -#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 +__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 -__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 9 "str.asm" + ld de, (SCREEN_ADDR) ; Adds the screen address + add hl, de + + ; Return current screen address in HL + ret -__STR: + ; 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: -__STR_FAST: + ; Checks for valid coords + call __IN_SCREEN + ret nc - PROC - LOCAL __STR_END - LOCAL RECLAIM2 - LOCAL STK_END +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC - 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 __ATTR_ADDR + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T - 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) + ld a, d + and (hl) + ld c, a ; C = current screen color, masked - pop hl - ld (ATTR_T), hl ; Restores ATTR_T - pop hl - ld (STK_END), hl ; Balance STK_END to avoid STR$ bug + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + + ret + + ENDP - push bc - push de - inc bc - inc bc - call __MEM_ALLOC ; HL Points to new block +#line 19 "print.asm" - pop de - pop bc + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. - push hl - ld a, h - or l - jr z, __STR_END ; Return if NO MEMORY (NULL) - push bc - push de - ld (hl), c - inc hl - ld (hl), b - inc hl ; Copies length +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC - ex de, hl ; HL = start of original string - ldir ; Copies string content + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl - pop de ; Original (ROM-CALC) string - pop bc ; Original Length - -__STR_END: - ex de, hl - inc bc + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY - call RECLAIM2 ; Frees TMP Memory - pop hl ; String result + xor a + ld (FLAGS2), a - ret + ret - RECLAIM2 EQU 19E8h - STK_END EQU 5C65h - ENDP +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A -#line 147 "lcd3.bas" -#line 1 "u32tofreg.asm" -#line 1 "neg32.asm" -__ABS32: - bit 7, d - ret z + LOCAL PO_GR_1 -__NEG32: ; Negates DEHL (Two's complement) - ld a, l - cpl - ld l, a + LOCAL __PRCHAR + LOCAL __PRINT_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START - ld a, h - cpl - ld h, a + PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 - ld a, e - cpl - ld e, a - - ld a, d - cpl - ld d, a +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - inc l - ret nz +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - inc h - ret nz + 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 - inc de - ret +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jp __PRGRAPH0 -#line 2 "u32tofreg.asm" -__I8TOFREG: - ld l, a - rlca - sbc a, a ; A = SGN(A) - ld h, a - ld e, a - ld d, a + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) -__I32TOFREG: ; Converts a 32bit signed integer (stored in DEHL) - ; to a Floating Point Number returned in (A ED CB) +__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 - ld a, d - or a ; Test sign +__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 - jp p, __U32TOFREG ; It was positive, proceed as 32bit unsigned +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 ; - call __NEG32 ; Convert it to positive - call __U32TOFREG ; Convert it to Floating point +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 - set 7, e ; Put the sign bit (negative) in the 31bit of mantissa - ret + ld (hl), a -__U8TOFREG: - ; Converts an unsigned 8 bit (A) to Floating point - ld l, a - ld h, 0 - ld e, h - ld d, h + inc de + inc h ; Next line + djnz __PRCHAR -__U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) - ; to a Floating point number returned in A ED CB + 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 - PROC +__PRINT_CONT: + call __SAVE_S_POSN - LOCAL __U32TOFREG_END +__PRINT_CONT2: + exx + ret - ld a, d - or e - or h - or l - ld b, d - ld c, e ; Returns 00 0000 0000 if ZERO - ret z + ; ------------- SPECIAL CHARS (< 32) ----------------- - push de - push hl +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A - 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 +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx -__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 +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + call __LOAD_S_POSN - srl b ; Shift B'C' D'E' >> 1, output bit stays in Carry - rr c - rr d - rr e - exx +__PRINT_EOL1: ; Another entry called from PRINT when next line required + ld e, 0 - rr e ; Shift EDCB >> 1, inserting the carry on the left - rr d - rr c - rr b +__PRINT_EOL2: + ld a, d + inc a - inc l ; Increment exponent - jp __U32TOFREG_LOOP +__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 -__U32TOFREG_END: - exx - ld a, l ; Puts the exponent in a - res 7, e ; Sets the sign bit to 0 (positive) +__PRINT_AT2_END: + call __SAVE_S_POSN + exx + ret - ret - ENDP +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret -#line 148 "lcd3.bas" -#line 1 "addf.asm" +__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 - ; ------------------------------------------------------------- - ; 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 - ; ------------------------------------------------------------- +__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 -__ADDF: ; Addition - call __FPSTACK_PUSH2 - - ; ------------- ROM ADD - rst 28h - defb 0fh ; ADD - defb 38h; ; END CALC +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE - jp __FPSTACK_POP +__PRINT_AT: + ld hl, __PRINT_AT1 -#line 149 "lcd3.bas" -#line 1 "printstr.asm" +__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 -#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 +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE - ; ----- IMPLEMENTATION NOTES ------ - ; The heap is implemented as a linked list of free blocks. +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART -; 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)| - ; +----------------+ +__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 - ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the - ; previous, or to the next (or both) they will be converted into a single - ; block (so defragmented). +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE - ; MEMORY MANAGER - ; - ; This library must be initialized calling __MEM_INIT with - ; HL = BLOCK Start & DE = Length. +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. +__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 - ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory - ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done - ; --------------------------------------------------------------------- +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART -MEM_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified - PROC +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART - ld a, h - or l - ret z ; Return if NULL pointer +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start -__MEM_LOOP2: +__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 hl ; Next block ptr + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + - ld e, (hl) +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next + 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 - ld a, h ; HL == NULL? - or l - jp z, __MEM_LINK_PREV; if so, link with previous +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 - 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 +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE - ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next + 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 -__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL - ex de, hl - push hl - dec hl +PRINT_AT: ; CHanges cursor to ROW, COL + ; COL in A register + ; ROW in stack - ld (hl), c - inc hl - ld (hl), b ; (DE) <- BC + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl - 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 __IN_SCREEN + ret nc ; Return if out of screen - call __MEM_JOIN_TEST - pop hl + jp __SAVE_S_POSN -__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) ; + 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 - 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 + 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 -__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 + ENDP - 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 +#line 149 "lcd3.bas" +#line 1 "printstr.asm" - 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 @@ -2236,7 +2045,6 @@ __PRINT_STR: ENDP #line 150 "lcd3.bas" - #line 1 "pstorestr2.asm" ; vim:ts=4:et:sw=4 ; @@ -2283,171 +2091,363 @@ __STORE_STR2: ld (hl), d dec hl ; HL points to mem address variable. This might be useful in the future. - ret + ret + +#line 9 "pstorestr2.asm" + +__PSTORE_STR2: + push ix + pop hl + add hl, bc + jp __STORE_STR2 + +#line 151 "lcd3.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 152 "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 + 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 153 "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$ + + 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$ -#line 9 "pstorestr2.asm" + ld a, h + or l + jr nz, __STR_CONT1 ; If len(a$) != 0 do copy -__PSTORE_STR2: - push ix - pop hl - add hl, bc - jp __STORE_STR2 + ; 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 -#line 152 "lcd3.bas" -#line 1 "ftou32reg.asm" +__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 -__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 + pop de ; DE = b$ - LOCAL __IS_FLOAT +__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) - or a - jr nz, __IS_FLOAT - ; Here if it is a ZX ROM Integer + push hl ; Saves HL to return it later - 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 + 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 -__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) + ex de, hl ; DE = end of string (Begin of copy addr) + ld c, (hl) + inc hl + ld b, (hl) ; BC = len(b$) - push de - push bc + ld a, b + or c + jr z, __STRCATEND; Return if len(b$) == 0 - exx - pop de ; Loads mantissa into C'B' E'D' - pop bc ; + push bc ; Save LEN(b$) + inc hl ; Skip 2nd byte of len(b$) + ldir ; Concatenate b$ - set 7, c ; Highest mantissa bit is always 1 - exx + 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, 0 ; DEHL = 0 - ld d, h - ld e, l + ld (hl), e ; Updates new LEN and return + inc hl + ld (hl), d + dec hl + ret - ;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) +__STRCATEND: + pop hl ; Removes Len(a$) + pop hl ; Restores original HL, so HL = a$ + ret - ld b, a ; Loop counter = exponent - 128 + ENDP -__FTOU32REG_LOOP: - exx ; Shift C'B' E'D' << 1, output bit stays in Carry - sla d - rl e - rl b - rl c +#line 154 "lcd3.bas" - exx ; Shift DEHL << 1, inserting the carry on the right - rl l - rl h - rl e - rl d +#line 1 "u32tofreg.asm" - djnz __FTOU32REG_LOOP +__I8TOFREG: + ld l, a + rlca + sbc a, a ; A = SGN(A) + ld h, a + ld e, a + ld d, a -__FTOU32REG_END: - pop af ; Take the sign bit - or a ; Sets SGN bit to 1 if negative - jp m, __NEG32 ; Negates DEHL - - ret +__I32TOFREG: ; Converts a 32bit signed integer (stored in DEHL) + ; to a Floating Point Number returned in (A ED CB) - ENDP + ld a, d + or a ; Test sign + jp p, __U32TOFREG ; It was positive, proceed as 32bit unsigned -__FTOU8: ; Converts float in C ED LH to Unsigned byte in A - call __FTOU32REG - ld a, l + 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 -#line 153 "lcd3.bas" -#line 1 "pushf.asm" +__U8TOFREG: + ; Converts an unsigned 8 bit (A) to Floating point + ld l, a + ld h, 0 + ld e, h + ld d, h - ; 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 +__U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) + ; to a Floating point number returned in A ED CB -__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 + PROC + LOCAL __U32TOFREG_END -#line 154 "lcd3.bas" -#line 1 "loadstr.asm" + 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 - ; Loads a string (ptr) from HL - ; and duplicates it on dynamic memory again - ; Finally, it returns result pointer in HL + exx + pop de ; Loads integer into B'C' D'E' + pop bc + exx -__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 + ld l, 128 ; Exponent + ld bc, 0 ; DEBC = 0 + ld d, b + ld e, c -__LOADSTR: ; __FASTCALL__ entry - ld a, h - or l - ret z ; Return if NULL +__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 - ld c, (hl) - inc hl - ld b, (hl) - dec hl ; BC = LEN(a$) + srl b ; Shift B'C' D'E' >> 1, output bit stays in Carry + rr c + rr d + rr e + exx - inc bc - inc bc ; BC = LEN(a$) + 2 (two bytes for length) + rr e ; Shift EDCB >> 1, inserting the carry on the left + rr d + rr c + rr b - push hl - push bc - call __MEM_ALLOC - pop bc ; Recover length - pop de ; Recover origin + inc l ; Increment exponent + jp __U32TOFREG_LOOP - 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 155 "lcd3.bas" +__U32TOFREG_END: + exx + ld a, l ; Puts the exponent in a + res 7, e ; Sets the sign bit to 0 (positive) + + ret + ENDP +#line 156 "lcd3.bas" ZXBASIC_USER_DATA: _adr: diff --git a/tests/functional/lcd7.asm b/tests/functional/lcd7.asm index d192928ae..36c1441a7 100644 --- a/tests/functional/lcd7.asm +++ b/tests/functional/lcd7.asm @@ -84,8 +84,7 @@ __LABEL0: DEFW 0002h DEFB 4Fh DEFB 4Bh -#line 1 "loadstr.asm" -#line 1 "alloc.asm" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -140,7 +139,7 @@ __LABEL0: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -153,47 +152,6 @@ __LABEL0: ; 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_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 @@ -320,7 +278,240 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" +#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 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) +; 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_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" + ; --------------------------------------------------------------------- @@ -478,8 +669,11 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result ret -#line 72 "lcd7.bas" -#line 1 "printstr.asm" +#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 @@ -1545,200 +1739,18 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#line 5 "print_eol_attr.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 +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 74 "lcd7.bas" +#line 1 "printstr.asm" - 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 @@ -1791,7 +1803,7 @@ __PRINT_STR: ENDP -#line 73 "lcd7.bas" +#line 75 "lcd7.bas" #line 1 "pstorestr.asm" ; vim:ts=4:et:sw=4 ; @@ -2108,18 +2120,6 @@ __PSTORE_STR: add hl, bc jp __STORE_STR -#line 74 "lcd7.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 76 "lcd7.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/lcd8.asm b/tests/functional/lcd8.asm index e86a8c0a0..dfde6ad6b 100644 --- a/tests/functional/lcd8.asm +++ b/tests/functional/lcd8.asm @@ -86,8 +86,7 @@ __LABEL0: DEFW 0002h DEFB 4Fh DEFB 4Bh -#line 1 "loadstr.asm" -#line 1 "alloc.asm" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -142,7 +141,7 @@ __LABEL0: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -155,47 +154,6 @@ __LABEL0: ; 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_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 @@ -322,7 +280,240 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" +#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 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) +; 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_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" + ; --------------------------------------------------------------------- @@ -480,8 +671,11 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result ret -#line 74 "lcd8.bas" -#line 1 "printstr.asm" +#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 @@ -1547,200 +1741,18 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes 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. - - - - ; --------------------------------------------------------------------- - ; 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 +#line 5 "print_eol_attr.asm" - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length - ld b, h - ld c, l ; BC = Total Length +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 76 "lcd8.bas" +#line 1 "printstr.asm" - 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 @@ -1793,18 +1805,6 @@ __PRINT_STR: ENDP -#line 75 "lcd8.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 77 "lcd8.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/lcd9.asm b/tests/functional/lcd9.asm index 0f105d6ad..2ec1ca09f 100644 --- a/tests/functional/lcd9.asm +++ b/tests/functional/lcd9.asm @@ -74,8 +74,7 @@ __LABEL0: DEFW 0002h DEFB 4Fh DEFB 4Bh -#line 1 "loadstr.asm" -#line 1 "alloc.asm" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -130,7 +129,7 @@ __LABEL0: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -143,47 +142,6 @@ __LABEL0: ; 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_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 @@ -310,7 +268,240 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" +#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 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) +; 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_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" + ; --------------------------------------------------------------------- @@ -468,8 +659,11 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result ret -#line 62 "lcd9.bas" -#line 1 "printstr.asm" +#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 @@ -1535,200 +1729,18 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#line 5 "print_eol_attr.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 +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 64 "lcd9.bas" +#line 1 "printstr.asm" - 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 @@ -1781,7 +1793,7 @@ __PRINT_STR: ENDP -#line 63 "lcd9.bas" +#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 @@ -2083,18 +2095,6 @@ __STORE_STR: pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret -#line 64 "lcd9.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 66 "lcd9.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/load02.asm b/tests/functional/load02.asm index f268ad514..415de1175 100644 --- a/tests/functional/load02.asm +++ b/tests/functional/load02.asm @@ -47,7 +47,7 @@ __LABEL0: DEFB 73h DEFB 74h DEFB 31h -#line 1 "loadstr.asm" +#line 1 "load.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -398,52 +398,7 @@ __MEM_SUBTRACT: 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 35 "load02.bas" -#line 1 "load.asm" - +#line 2 "load.asm" #line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -1946,6 +1901,51 @@ PRINT_TAPE_MSG: 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 + 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 36 "load02.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/load03.asm b/tests/functional/load03.asm index 12fef2d14..ecd56d9bb 100644 --- a/tests/functional/load03.asm +++ b/tests/functional/load03.asm @@ -46,7 +46,7 @@ __LABEL0: DEFB 65h DEFB 73h DEFB 74h -#line 1 "loadstr.asm" +#line 1 "load.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -397,52 +397,7 @@ __MEM_SUBTRACT: 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 34 "load03.bas" -#line 1 "load.asm" - +#line 2 "load.asm" #line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -1945,6 +1900,51 @@ PRINT_TAPE_MSG: 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 + 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 35 "load03.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/loadstr.asm b/tests/functional/loadstr.asm index 3456d4954..23e066ea1 100644 --- a/tests/functional/loadstr.asm +++ b/tests/functional/loadstr.asm @@ -400,9 +400,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -926,7 +926,13 @@ __STORE_STR2: ret #line 33 "loadstr.bas" -#line 1 "val.asm" +#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" ; ------------------------------------------------------------- @@ -974,7 +980,91 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK xor a ld b, a jp __FPSTACK_PUSH -#line 3 "val.asm" +#line 9 "str.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 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 + 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 @@ -1092,96 +1182,6 @@ __RET_ZERO: ; Returns 0 Floating point on error ENDP -#line 34 "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 "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 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 - 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 35 "loadstr.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/ltee1.asm b/tests/functional/ltee1.asm index 244813310..cbd832bdc 100644 --- a/tests/functional/ltee1.asm +++ b/tests/functional/ltee1.asm @@ -100,28 +100,7 @@ __LABEL1: DEFB 62h DEFB 6Ch DEFB 65h -#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$) - ; 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" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -189,117 +168,6 @@ __LABEL1: ; 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_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 @@ -426,123 +294,131 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" - +#line 69 "free.asm" ; --------------------------------------------------------------------- - ; MEM_ALLOC - ; Allocates a block of memory in the heap. - ; - ; Parameters - ; BC = Length of requested memory block + ; MEM_FREE + ; Frees a block of memory ; -; Returns: - ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) - ; if the block could not be allocated (out of memory) +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done ; --------------------------------------------------------------------- -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified PROC - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - TEMP EQU TEMP0 + 1 + ld a, h + or l + ret z ; Return if NULL pointer - ld hl, 0 - ld (TEMP), hl + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__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" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) + +__MEM_LOOP2: 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 + inc hl ; Next block ptr - 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 d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next - ld (hl), e - inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) + 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), 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 + ld (hl), c inc hl - ld (hl), b - inc hl ; Return 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 + ENDP -#line 71 "realloc.asm" -#line 1 "free.asm" +#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) @@ -597,7 +473,7 @@ __MEM_SUBTRACT: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -610,354 +486,206 @@ __MEM_SUBTRACT: ; 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 - ; --------------------------------------------------------------------- - ; 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 + ; Error code definitions (as in ZX spectrum manual) - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - ld a, h - or l - ret z ; Return if NULL pointer - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next + ; 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" - 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_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_LINK_PREV: ; Link (DE) with BC, and BC->next with HL - ex de, hl - push hl - dec hl +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC - ld (hl), c - inc hl - ld (hl), b ; (DE) <- BC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - 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 + TEMP EQU TEMP0 + 1 - call __MEM_JOIN_TEST - pop hl + ld hl, 0 + ld (TEMP), hl -__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them - ; hl = Ptr to current block + 2 - ld d, (hl) - dec 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" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ; HL = Pointer to Free block ld e, (hl) - dec hl - ld b, (hl) ; Loads block length into BC - dec hl - ld c, (hl) ; + inc hl + ld d, (hl) + inc hl ; DE = Block Length - 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 + 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 - ret nz + ld (TEMP), hl -__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 + ld e, (hl) 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) + 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 d, (hl) ; DE = block->next + 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 - 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 + ld (hl), d ; LINKED + pop hl ; Returning block. + 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 +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec 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 + ld (hl), e ; Store new block length + + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack - dec hl ; Set HL - dec hl ; To begin of block + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl ret - + ENDP -#line 2 "strcpy.asm" - - ; String library +#line 2 "loadstr.asm" -__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) - PROC + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL - LOCAL __STRREALLOC - LOCAL __STRCONTINUE - LOCAL __B_IS_NULL - LOCAL __NOTHING_TO_COPY +__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 - ld b, d - ld c, e - ld a, b - or c - jr z, __B_IS_NULL +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL - ex de, hl ld c, (hl) inc hl ld b, (hl) - dec hl ; BC = LEN(b$) - ex de, hl ; DE = &b$ + dec hl ; BC = LEN(a$) -__B_IS_NULL: ; Jumps here if B$ pointer is NULL inc bc - inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + inc bc ; BC = LEN(a$) + 2 (two bytes for length) - push de push hl + push bc + call __MEM_ALLOC + pop bc ; Recover length + pop de ; Recover origin 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 + ret z ; Return if NULL (No memory) -__NOTHING_TO_COPY: - ex de, hl - ld (hl), e - inc hl - ld (hl), d - dec hl + 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 - - 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 88 "ltee1.bas" - +#line 89 "ltee1.bas" #line 1 "print_eol_attr.asm" ; Calls PRINT_EOL and then COPY_ATTR, so saves ; 3 bytes @@ -1939,101 +1667,484 @@ PRINT_COMMA: and 16 add a, 16 -PRINT_TAB: - PROC - LOCAL LOOP, CONTINUE +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 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 + ; 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 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$) + ; 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 - 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 +#line 14 "storestr.asm" -PRINT_AT: ; CHanges cursor to ROW, COL - ; COL in A register - ; ROW in stack +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc - pop hl ; Ret address - ex (sp), hl ; callee H = ROW - ld l, a - ex de, hl +__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) - call __IN_SCREEN - ret nc ; Return if out of screen +__STORE_STR: + push de ; Pointer to b$ + push hl ; Array pointer to variable memory address - jp __SAVE_S_POSN + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) - 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 + 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 - ENDP - + ld (hl), e + inc hl + ld (hl), d ; Stores a$ ptr into elemem ptr -#line 5 "print_eol_attr.asm" + 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) + ; -PRINT_EOL_ATTR: - call PRINT_EOL - jp COPY_ATTR -#line 90 "ltee1.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. @@ -2074,7 +2185,16 @@ __STORE_STR2: ret -#line 91 "ltee1.bas" +#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" @@ -2219,126 +2339,6 @@ __STRCATEND: ENDP -#line 92 "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 - ; 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 93 "ltee1.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 - 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 94 "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) - ; - - - -__PSTORE_STR2: - push ix - pop hl - add hl, bc - jp __STORE_STR2 - #line 95 "ltee1.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/ltee3.asm b/tests/functional/ltee3.asm index b4ca89cd9..97dcb45c7 100644 --- a/tests/functional/ltee3.asm +++ b/tests/functional/ltee3.asm @@ -527,9 +527,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/ltee5.asm b/tests/functional/ltee5.asm index 7498d7183..439d77e5a 100644 --- a/tests/functional/ltee5.asm +++ b/tests/functional/ltee5.asm @@ -73,28 +73,75 @@ __LABEL1: DEFB 63h DEFB 61h DEFB 6Ch -#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 "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 ; - -#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) + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). ; - ; e.g. => HL = _variableName (DIM _variableName$) - ; DE = Address into the HEAP + ; Please read the BSD license 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 function will resize (REALLOC) the space pointed by HL - ; before copying the content of b$ into a$ + ; This library must be initialized 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 "strcpy.asm" -#line 1 "realloc.asm" +#line 1 "heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -163,48 +210,208 @@ __LABEL1: ; 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 + ; --------------------------------------------------------------------- + ; __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 - ; Error code definitions (as in ZX spectrum manual) + 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 -; Set error code with: - ; ld a, ERROR_CODE - ; ld (ERR_NR), a + 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 - 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_InvalidFileName EQU 14 - ERROR_InvalidColour EQU 19 - ERROR_BreakIntoProgram EQU 20 - ERROR_TapeLoadingErr EQU 26 + 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 - ; Raises error using RST #8 -__ERROR: - ld (__ERROR_CODE), a - rst 8 -__ERROR_CODE: - nop - ret + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a - ; 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" + 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 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$) + ; 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) @@ -259,7 +466,7 @@ __STOP: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -273,7 +480,48 @@ __STOP: ; They will be added automatically if needed. -#line 1 "heapinit.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_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) @@ -328,7 +576,7 @@ __STOP: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -344,63 +592,6 @@ __STOP: - ; --------------------------------------------------------------------- - ; __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 @@ -437,9 +628,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -515,197 +706,7 @@ __MEM_SUBTRACT: #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" ; --------------------------------------------------------------------- @@ -929,8 +930,7 @@ __PSTORE_STR: add hl, bc jp __STORE_STR -#line 62 "ltee5.bas" - +#line 63 "ltee5.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/ltee6.asm b/tests/functional/ltee6.asm index 56558aa55..47a96d7f7 100644 --- a/tests/functional/ltee6.asm +++ b/tests/functional/ltee6.asm @@ -45,6 +45,207 @@ __LABEL0: DEFB 62h 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) +; 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/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" + +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +LOOP: + pop bc ; Get next index (Ai) from the stack + +#line 60 "/Users/boriel/Documents/src/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) + +#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 + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 100 "/Users/boriel/Documents/src/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 131 "/Users/boriel/Documents/src/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 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 @@ -402,9 +603,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -886,207 +1087,6 @@ __STORE_STR: pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret -#line 34 "ltee6.bas" -#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/spyder/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" - -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - -LOOP: - pop bc ; Get next index (Ai) from the stack - -#line 60 "/Users/boriel/Documents/src/spyder/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) - -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - ;call __MUL16_FAST ; HL *= DE - call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - jp LOOP - -ARRAY_END: - ld e, (hl) - inc hl - ld d, c ; C = 0 => DE = E = Element size - push hl - push de - exx - -#line 100 "/Users/boriel/Documents/src/spyder/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 131 "/Users/boriel/Documents/src/spyder/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 35 "ltee6.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/ltee7.asm b/tests/functional/ltee7.asm index 94e07fb50..a2dcfaacd 100644 --- a/tests/functional/ltee7.asm +++ b/tests/functional/ltee7.asm @@ -83,21 +83,213 @@ __LABEL0: DEFB 6Ch 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$) - ; 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 "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 "strcpy.asm" -#line 1 "realloc.asm" +#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/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" + +#line 48 "/Users/boriel/Documents/src/zxbasic/library-asm/array.asm" + +LOOP: + pop bc ; Get next index (Ai) from the stack + +#line 60 "/Users/boriel/Documents/src/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) + +#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 + ld d, c ; C = 0 => DE = E = Element size + push hl + push de + exx + +#line 100 "/Users/boriel/Documents/src/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 131 "/Users/boriel/Documents/src/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 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) @@ -165,118 +357,7 @@ __LABEL0: ; 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_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" +#line 1 "heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -402,191 +483,7 @@ __MEM_INIT2: 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/spyder/zxbasic/library-asm/alloc.asm" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/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 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. - - +#line 69 "free.asm" ; --------------------------------------------------------------------- ; MEM_FREE @@ -708,232 +605,10 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ENDP -#line 72 "realloc.asm" +#line 6 "arrayfree.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 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 - - - -__ARRAY_FREE: - PROC +__ARRAY_FREE: + PROC LOCAL __ARRAY_LOOP @@ -996,206 +671,531 @@ __ARRAY_FREE_MEM: ; like the above, buf also frees the array itself jp __MEM_FREE ; Frees it and returns from __MEM_FREE #line 73 "ltee7.bas" -#line 1 "array.asm" +#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 - ; ------------------------------------------------------------------- - ; 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 + ... + ; + ; 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. -#line 1 "mul16.asm" -__MUL16: ; Mutiplies HL with the last value stored into de stack - ; Works for both signed and unsigned +; 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)| + ; +----------------+ - PROC - LOCAL __MUL16LOOP - LOCAL __MUL16NOADD - - ex de, hl - pop hl ; Return address - ex (sp), hl ; CALLEE caller convention + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the 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_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. + + + + + + ; --------------------------------------------------------------------- + ; 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/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/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 71 "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 -;;__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 + 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 -__MUL16_FAST: - ld b, 16 - ld a, d - ld c, e - ex de, hl - ld hl, 0 + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) -__MUL16LOOP: - add hl, hl ; hl << 1 - sla c - rla ; a,c << 1 - jp nc, __MUL16NOADD - add hl, de + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) -__MUL16NOADD: - djnz __MUL16LOOP + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block - ret ; Result in hl (16 lower bits) + 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 - ENDP +__REALLOC_END: -#line 20 "array.asm" + dec hl ; Set HL + dec hl ; To begin of block + ret -#line 24 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" + ENDP -__ARRAY: - PROC +#line 2 "strcpy.asm" - LOCAL LOOP - LOCAL ARRAY_END - LOCAL RET_ADDRESS ; Stores return address + ; String library - 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" +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC -#line 48 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY -LOOP: - pop bc ; Get next index (Ai) from the stack + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL -#line 60 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ - add hl, bc ; Adds current index +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) - 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 + push de + push hl - 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) + ld a, h + or l + jr z, __STRREALLOC -#line 80 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - ;call __MUL16_FAST ; HL *= DE - call __FNMUL -#line 86 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - jp LOOP - -ARRAY_END: - ld e, (hl) - inc hl - ld d, c ; C = 0 => DE = E = Element size - push hl - push de - exx + 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) -#line 100 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" - LOCAL ARRAY_SIZE_LOOP + 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 - ex de, hl - ld hl, 0 - pop bc - ld b, c -ARRAY_SIZE_LOOP: - add hl, de - djnz ARRAY_SIZE_LOOP + 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 - ;; Even faster - ;pop bc +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl - ;ld d, h - ;ld e, l - - ;dec c - ;jp z, __ARRAY_FIN +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ - ;add hl, hl - ;dec c - ;jp z, __ARRAY_FIN + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) - ;add hl, hl - ;dec c - ;dec c - ;jp z, __ARRAY_FIN +__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 - ;add hl, de - ;__ARRAY_FIN: -#line 131 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/array.asm" +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret - pop de - add hl, de ; Adds element start + ENDP -RET_ADDRESS: - ld de, 0 - push de - ret ; HL = (Start of Elements + Offset) +#line 14 "storestr.asm" - ;; Performs a faster multiply for little 16bit numbs - LOCAL __FNMUL, __FNMUL2 +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc -__FNMUL: - xor a - or d - jp nz, __MUL16_FAST +__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) - or e - ex de, hl - ret z +__STORE_STR: + push de ; Pointer to b$ + push hl ; Array pointer to variable memory address - cp 33 - jp nc, __MUL16_FAST + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) - ld b, l - ld l, h ; HL = 0 + 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 -__FNMUL2: - add hl, de - djnz __FNMUL2 + 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 - ENDP - #line 74 "ltee7.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/opt3_einar.asm b/tests/functional/opt3_einar.asm index 4449b0997..703001275 100644 --- a/tests/functional/opt3_einar.asm +++ b/tests/functional/opt3_einar.asm @@ -56,16 +56,15 @@ _x2__leave: ld sp, ix pop ix ret -__LABEL3: - DEFW 0002h - DEFB 4Fh - DEFB 4Bh __LABEL2: DEFW 0003h DEFB 4Fh DEFB 70h DEFB 73h -#line 1 "printstr.asm" +__LABEL3: + DEFW 0002h + DEFB 4Fh + DEFB 4Bh #line 1 "print.asm" ; vim:ts=4:sw=4:et: ; PRINT command routine @@ -409,7 +408,7 @@ BRIGHT_TMP: #line 1 "copy_attr.asm" -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" +#line 4 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" @@ -468,7 +467,7 @@ TABLE: and (hl) ; OVER 2 MODE or (hl) ; OVER 3 MODE -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" +#line 65 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" __REFRESH_TMP: ld a, (hl) @@ -1171,7 +1170,9 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#line 53 "opt3_einar.bas" +#line 1 "printstr.asm" + #line 1 "free.asm" @@ -1543,8 +1544,7 @@ __PRINT_STR: ENDP -#line 53 "opt3_einar.bas" - +#line 54 "opt3_einar.bas" ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: diff --git a/tests/functional/opt3_lcd5.asm b/tests/functional/opt3_lcd5.asm index 942c63d13..ba7784d3b 100644 --- a/tests/functional/opt3_lcd5.asm +++ b/tests/functional/opt3_lcd5.asm @@ -385,53 +385,19 @@ _ScanNear__leave: ex (sp), hl exx ret -#line 1 "lei16.asm" - -#line 1 "lti8.asm" - -__LTI8: ; Test 8 bit values A < H - ; Returns result in A: 0 = False, !0 = True - sub h - -__LTI: ; Signed CMP - PROC - LOCAL __PE - - ld a, 0 ; Sets default to false -__LTI2: - jp pe, __PE - ; Overflow flag NOT set - ret p - dec a ; TRUE - -__PE: ; Overflow set - ret m - dec a ; TRUE - ret - - ENDP -#line 3 "lei16.asm" +#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 -__LEI16: ; Test 8 bit values HL < DE - ; Returns result in A: 0 = False, !0 = True - xor a - sbc hl, de - jp nz, __LTI2 - dec a - ret +__AND8: + or a + ret z + ld a, h + ret #line 377 "opt3_lcd5.bas" -#line 1 "lti16.asm" - - - -__LTI16: ; Test 8 bit values HL < DE - ; Returns result in A: 0 = False, !0 = True - xor a - sbc hl, de - jp __LTI2 - -#line 378 "opt3_lcd5.bas" #line 1 "ftou32reg.asm" #line 1 "neg32.asm" __ABS32: @@ -540,18 +506,52 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ld a, l ret +#line 378 "opt3_lcd5.bas" +#line 1 "lei16.asm" + +#line 1 "lti8.asm" + +__LTI8: ; Test 8 bit values A < H + ; Returns result in A: 0 = False, !0 = True + sub h + +__LTI: ; Signed CMP + PROC + LOCAL __PE + + ld a, 0 ; Sets default to false +__LTI2: + jp pe, __PE + ; Overflow flag NOT set + ret p + dec a ; TRUE + +__PE: ; Overflow set + ret m + dec a ; TRUE + ret + + ENDP +#line 3 "lei16.asm" + +__LEI16: ; Test 8 bit values HL < DE + ; Returns result in A: 0 = False, !0 = True + xor a + sbc hl, de + jp nz, __LTI2 + dec a + ret + #line 379 "opt3_lcd5.bas" -#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 +#line 1 "lti16.asm" -__AND8: - or a - ret z - ld a, h - ret + + +__LTI16: ; Test 8 bit values HL < DE + ; Returns result in A: 0 = False, !0 = True + xor a + sbc hl, de + jp __LTI2 #line 380 "opt3_lcd5.bas" diff --git a/tests/functional/opt3_proc0.asm b/tests/functional/opt3_proc0.asm index 1537d5468..23fac3545 100644 --- a/tests/functional/opt3_proc0.asm +++ b/tests/functional/opt3_proc0.asm @@ -39,8 +39,10 @@ PROC0.isAt: ADD HL, BC EX DE,HL INC HL + LD D,(HL) DEC BC INC HL + LD E,(HL) DEC BC PROC0.isNewline: ENDP diff --git a/tests/functional/opt3_sp.asm b/tests/functional/opt3_sp.asm index a07a7f3a2..94679ef6f 100644 --- a/tests/functional/opt3_sp.asm +++ b/tests/functional/opt3_sp.asm @@ -146,6 +146,50 @@ __ADDF: ; Addition 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) + 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 70 "opt3_sp.bas" #line 1 "pstoref.asm" ; Stores FP number in A ED CB at location HL+IX ; HL = Offset @@ -193,50 +237,6 @@ __PSTOREF: pop de jp __STOREF -#line 70 "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) - 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 71 "opt3_sp.bas" #line 1 "u32tofreg.asm" #line 1 "neg32.asm" diff --git a/tests/functional/param0.asm b/tests/functional/param0.asm index f16af729f..b71915f90 100644 --- a/tests/functional/param0.asm +++ b/tests/functional/param0.asm @@ -62,1438 +62,1669 @@ _test__leave: ex (sp), hl exx ret -__LABEL1: - DEFW 0001h - DEFB 41h __LABEL0: DEFW 0001h DEFB 48h -#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 "sposn.asm" - ; Printing positioning library. - PROC - LOCAL ECHO_E +__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) +; 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 -__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 - + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. -__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 +; 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)| + ; +----------------+ - ECHO_E EQU 23682 - MAXX EQU ECHO_E ; Max X position + 1 - MAXY EQU MAXX + 1 ; Max Y position + 1 + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). - S_POSN EQU 23688 - POSX EQU S_POSN ; Current POS X - POSY EQU S_POSN + 1 ; Current POS Y - ENDP + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. -#line 6 "print.asm" -#line 1 "cls.asm" - ; JUMPS directly to spectrum CLS - ; This routine does not clear lower screen + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - ;CLS EQU 0DAFh +#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 - ; Our faster implementation + ; ----- 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)| + ; +----------------+ -CLS: - PROC + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). - 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 + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - ; Now clear attributes + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - 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" + ; --------------------------------------------------------------------- + ; __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 -#line 1 "error.asm" - ; Simple error control routines -; vim:ts=4:et: + ; --------------------------------------------------------------------- + ; __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 - ERR_NR EQU 23610 ; Error code system variable + 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 - ; Error code definitions (as in ZX spectrum manual) + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block -; Set error code with: - ; ld a, ERROR_CODE - ; ld (ERR_NR), a + 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 - 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_InvalidFileName EQU 14 - ERROR_InvalidColour EQU 19 - ERROR_BreakIntoProgram EQU 20 - ERROR_TapeLoadingErr EQU 26 + 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 - ; 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) + ENDP -__IN_SCREEN_ERR: -__OUT_OF_SCREEN_ERR: - ; Jumps here if out of screen - ld a, ERROR_OutOfScreen - jp __STOP ; Saves error code and exits +#line 69 "free.asm" - ENDP -#line 8 "print.asm" -#line 1 "table_jump.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 + ; --------------------------------------------------------------------- -JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A - add a, a +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC -JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE - ld e, a - ld d, 0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN -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) + ld a, h + or l + ret z ; Return if NULL pointer -#line 9 "print.asm" -#line 1 "ink.asm" - ; Sets ink color in ATTR_P permanently -; Parameter: Paper color in A register + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -#line 1 "const.asm" - ; Global constants + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start - 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 +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr -#line 5 "ink.asm" + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next -INK: - PROC - LOCAL __SET_INK - LOCAL __SET_INK2 + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous - ld de, ATTR_P + 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 -__SET_INK: - cp 8 - jr nz, __SET_INK2 + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next - 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 +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl -__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 + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC - ; Sets the INK color passed in A register in the ATTR_T variable -INK_TMP: - ld de, ATTR_T - jp __SET_INK + 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 - ENDP + call __MEM_JOIN_TEST + pop hl -#line 10 "print.asm" -#line 1 "paper.asm" - ; Sets paper color in ATTR_P permanently -; Parameter: Paper color in A register +__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 -PAPER: - PROC - LOCAL __SET_PAPER - LOCAL __SET_PAPER2 - - ld de, ATTR_P + ld b, h + ld c, l ; BC = Total Length -__SET_PAPER: - cp 8 - jr nz, __SET_PAPER2 - inc de - ld a, (de) - or 038h - ld (de), a - ret + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next - ; Another entry. This will set the paper color at location pointer by DE -__SET_PAPER2: - and 7 ; # Remove - rlca - rlca - rlca ; a *= 8 + 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 - 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 + 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) +; 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 - ; 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 + ; ----- 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)| + ; +----------------+ -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 + ; When 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). - rrca - ld b, a ; Saves the color - ld a, (de) - and 07Fh ; Clears previous value - or b - ld (de), a - ret + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - ; Sets the FLASH flag passed in A register in the ATTR_T variable -FLASH_TMP: - ld de, ATTR_T - jr __SET_FLASH + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. -#line 12 "print.asm" -#line 1 "bright.asm" - ; Sets bright flag in ATTR_P permanently -; Parameter: Paper color in A register +#line 1 "error.asm" + ; Simple error control routines +; vim:ts=4:et: + ERR_NR EQU 23610 ; Error code system variable -BRIGHT: - ld de, ATTR_P + ; Error code definitions (as in ZX spectrum manual) -__SET_BRIGHT: - ; Another entry. This will set the bright flag at location pointer by DE - and 1 ; # Convert to 0/1 +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - rrca - rrca - ld b, a ; Saves the color - ld a, (de) - and 0BFh ; Clears previous value - or b - ld (de), a - ret + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - ; 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" + ; 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 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.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) + ; --------------------------------------------------------------------- -COPY_ATTR: - ; Just copies current permanent attribs to temporal attribs - ; and sets print mode - PROC +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC - LOCAL INVERSE1 - LOCAL __REFRESH_TMP + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - INVERSE1 EQU 02Fh + TEMP EQU TEMP0 + 1 - ld hl, (ATTR_P) - ld (ATTR_T), hl + ld hl, 0 + ld (TEMP), hl - ld hl, FLAGS2 - call __REFRESH_TMP - - ld hl, P_FLAG - call __REFRESH_TMP +__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" + ret z ; NULL +#line 113 "/home/boriel/Documents/src/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 -__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + 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 - LOCAL TABLE - LOCAL CONT2 + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + + ENDP - 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 +#line 2 "loadstr.asm" - ld hl, TABLE - add hl, bc - ld a, (hl) - ld (PRINT_MODE), a + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL - 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 +__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 -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL -__REFRESH_TMP: - ld a, (hl) - and 10101010b - ld c, a - rra - or c - ld (hl), a - ret + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(a$) - ENDP + inc bc + inc bc ; BC = LEN(a$) + 2 (two bytes for length) -#line 4 "over.asm" + 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) -OVER: - PROC + 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 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 - ld c, a ; saves it for later - and 2 - ld hl, FLAGS2 - res 1, (HL) - or (hl) - ld (hl), a +#line 1 "sposn.asm" + ; Printing positioning library. + PROC + LOCAL ECHO_E - ld a, c ; Recovers previous value - and 1 ; # Convert to 0/1 - add a, a; # Shift left 1 bit for permanent +__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 + - ld hl, P_FLAG - res 1, (hl) - or (hl) - ld (hl), a - 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 - ; 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 + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 - ENDP + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y -#line 14 "print.asm" -#line 1 "inverse.asm" - ; Sets INVERSE flag in P_FLAG permanently -; Parameter: INVERSE flag in bit 0 of A register + ENDP +#line 6 "print.asm" +#line 1 "cls.asm" + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + ;CLS EQU 0DAFh -INVERSE: - PROC + ; Our faster implementation - 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 +CLS: + PROC -#line 15 "print.asm" -#line 1 "bold.asm" - ; Sets BOLD flag in P_FLAG permanently -; Parameter: BOLD flag in bit 0 of A register + 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 -BOLD: - PROC + ; Now clear attributes - and 1 - rlca - rlca - rlca - ld hl, FLAGS2 - res 3, (HL) - or (hl) + ld a, (ATTR_P) ld (hl), a + ld bc, 767 + ldir 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 + 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 16 "print.asm" -#line 1 "italic.asm" - ; Sets ITALIC flag in P_FLAG permanently -; Parameter: ITALIC flag in bit 0 of A register +#line 7 "print.asm" +#line 1 "in_screen.asm" -ITALIC: - PROC - and 1 - rrca - rrca - rrca - ld hl, FLAGS2 - res 5, (HL) - or (hl) - ld (hl), a - ret +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) - ; 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 + PROC + LOCAL __IN_SCREEN_ERR - ENDP + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range -#line 17 "print.asm" - -#line 1 "attr.asm" - ; Attribute routines -; vim:ts=4:et:sw: + 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 -__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 +#line 1 "const.asm" + ; Global constants - 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 + 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 - ld de, (SCREEN_ADDR) ; Adds the screen address - add hl, de - - ; Return current screen address in HL - ret +#line 5 "ink.asm" +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 - ; 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: + ld de, ATTR_P - ; Checks for valid coords - call __IN_SCREEN - ret nc +__SET_INK: + cp 8 + jr nz, __SET_INK2 -__SET_ATTR: - ; Internal __FASTCALL__ Entry used by printing routines - PROC + 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 - call __ATTR_ADDR - ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T +__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 - ld a, d - and (hl) - ld c, a ; C = current screen color, masked + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK - ld a, d - cpl ; Negate mask - and e ; Mask current attributes - or c ; Mix them - ld (hl), a ; Store result in screen - - ret - - ENDP + ENDP +#line 10 "print.asm" +#line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register -#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. +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + + ld de, ATTR_P -__PRINT_INIT: ; To be called before program starts (initializes library) - PROC +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret - ld hl, __PRINT_START - ld (PRINT_JUMP_STATE), hl + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 - ld hl, 1821h - ld (MAXX), hl ; Sets current maxX and maxY + 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 - xor a - ld (FLAGS2), 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 -__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 +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 - PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret -__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 + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH - exx ; Switch to alternative registers - ex af, af' ; Saves a value (char to print) for later +#line 12 "print.asm" +#line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register - 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 +BRIGHT: + ld de, ATTR_P - ld a, c ; Recovers it - and 07h ; MOD 7 ; y1 - rrca - rrca - rrca +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 - or e - ld e, a - add hl, de ; HL = Screen address + DE - ex de, hl ; DE = Screen address - - ex af, af' + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret - cp 80h ; Is it an UDG or a ? - jp c, __SRCADDR - cp 90h - jp nc, __PRINT_UDG + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT - ; Print a 8 bit pattern (80h to 8Fh) +#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" - 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 +#line 4 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" -__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 ; +COPY_ATTR: + ; Just copies current permanent attribs to temporal attribs + ; and sets print mode + PROC -INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 - nop ; 2F -> CPL -> INVERSE 1 + LOCAL INVERSE1 + LOCAL __REFRESH_TMP - ld (hl), a + INVERSE1 EQU 02Fh - inc de - inc h ; Next line - djnz __PRCHAR + ld hl, (ATTR_P) + ld (ATTR_T), hl - 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 + ld hl, FLAGS2 + call __REFRESH_TMP + + ld hl, P_FLAG + call __REFRESH_TMP -__PRINT_CONT: - call __SAVE_S_POSN -__PRINT_CONT2: - exx - ret +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) - ; ------------- SPECIAL CHARS (< 32) ----------------- -__PRINT_SPECIAL: ; Jumps here if it is a special char - exx - ld hl, __PRINT_TABLE - jp JUMP_HL_PLUS_2A + 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) -PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence - exx + ld c, a + ld b, 0 -__PRINT_0Dh: ; Called WHEN printing CHR$(13) - call __LOAD_S_POSN + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a -__PRINT_EOL1: ; Another entry called from PRINT when next line required - ld e, 0 + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 -__PRINT_EOL2: - ld a, d - inc a +CONT2: + ld (INVERSE_MODE), a + ret -__PRINT_AT1_END: - ld hl, (MAXY) - cp l - jr c, __PRINT_EOL_END ; Carry if (MAXY) < d - xor a +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE -__PRINT_EOL_END: - ld d, a +#line 65 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" -__PRINT_AT2_END: - call __SAVE_S_POSN - exx - ret +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret -__PRINT_COM: - exx - push hl - push de - push bc - call PRINT_COMMA - pop bc - pop de - pop hl - ret + ENDP -__PRINT_TAB: - ld hl, __PRINT_TAB1 - jp __PRINT_SET_STATE +#line 4 "over.asm" -__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 +OVER: + PROC -__PRINT_NOP: -__PRINT_RESTART: - ld hl, __PRINT_START - jp __PRINT_SET_STATE + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a -__PRINT_AT: - ld hl, __PRINT_AT1 + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent -__PRINT_SET_STATE: - ld (PRINT_JUMP_STATE), hl ; Saves next entry call - exx - ret + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + 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 + ; 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 -__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 + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE -__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 + ENDP -__PRINT_INK: - ld hl, __PRINT_INK2 - jp __PRINT_SET_STATE +#line 14 "print.asm" +#line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register -__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 +INVERSE: + PROC -__PRINT_FLA2: - exx - call FLASH_TMP - jp __PRINT_RESTART + 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 -__PRINT_BRI: - ld hl, __PRINT_BRI2 - jp __PRINT_SET_STATE + ; 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 -__PRINT_BRI2: - exx - call BRIGHT_TMP - jp __PRINT_RESTART + ENDP -__PRINT_INV: - ld hl, __PRINT_INV2 - jp __PRINT_SET_STATE +#line 15 "print.asm" +#line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register -__PRINT_INV2: - exx - call INVERSE_TMP - jp __PRINT_RESTART -__PRINT_OVR: - ld hl, __PRINT_OVR2 - jp __PRINT_SET_STATE +BOLD: + PROC -__PRINT_OVR2: - exx - call OVER_TMP - jp __PRINT_RESTART + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret -__PRINT_BOLD: - ld hl, __PRINT_BOLD2 - jp __PRINT_SET_STATE + ; 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 -__PRINT_BOLD2: - exx - call BOLD_TMP - jp __PRINT_RESTART + ENDP -__PRINT_ITA: - ld hl, __PRINT_ITA2 - jp __PRINT_SET_STATE +#line 16 "print.asm" +#line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register -__PRINT_ITA2: - exx - call ITALIC_TMP - jp __PRINT_RESTART +ITALIC: + PROC -__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 - + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + 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 + ; 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 -PRINT_COMMA: - call __LOAD_S_POSN - ld a, e - and 16 - add a, 16 + ENDP -PRINT_TAB: - PROC - LOCAL LOOP, CONTINUE +#line 17 "print.asm" - 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 +#line 1 "attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: -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" +__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 -#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 + ld de, (SCREEN_ADDR) ; Adds the screen address + add hl, de + + ; Return current screen address in HL + ret - ; ----- 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)| - ; +----------------+ + ; 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 - ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the - ; previous, or to the next (or both) they will be converted into a single - ; block (so defragmented). +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + call __ATTR_ADDR + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T - ; MEMORY MANAGER - ; - ; This library must be initialized calling __MEM_INIT with - ; HL = BLOCK Start & DE = Length. + ld a, d + and (hl) + ld c, a ; C = current screen color, masked - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. + 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 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. +#line 19 "print.asm" -; 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)| - ; +----------------+ + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. - ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the - ; previous, or to the next (or both) they will be converted into a single - ; block (so defragmented). +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl - ; MEMORY MANAGER - ; - ; This library must be initialized calling __MEM_INIT with - ; HL = BLOCK Start & DE = Length. + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. + xor a + ld (FLAGS2), a + ret +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A - ; --------------------------------------------------------------------- - ; __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 + LOCAL PO_GR_1 - ; --------------------------------------------------------------------- - ; __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 + LOCAL __PRCHAR + LOCAL __PRINT_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START - 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 + 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 - 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 + inc de + inc h ; Next line + djnz __PRCHAR - ld (hl), c - inc hl - ld (hl), b - inc hl ; Pointer to next block + 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 - 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 +__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 - ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) - inc hl - ld (hl), a +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART - ld a, 201 - ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again - ret +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART - ENDP +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE -#line 69 "free.asm" +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART - ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory - ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done - ; --------------------------------------------------------------------- +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE -MEM_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified - PROC +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE - ld a, h - or l - ret z ; Return if NULL pointer +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART - ld a, h ; HL == NULL? - or l - jp z, __MEM_LINK_PREV; if so, link with previous +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE - 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 +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART - ;------ 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 +__BOLD: push hl - dec hl - - ld (hl), c + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a inc hl - ld (hl), b ; (DE) <- BC + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + - ld h, b ; HL <- BC (Free block ptr) - ld l, c - inc hl ; Skip block length (2 bytes) +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) inc hl - ld (hl), e ; Block->next = DE + srl (hl) inc hl - ld (hl), d - ; --- LINKED ; HL = &(BC->next) + 2 - - call __MEM_JOIN_TEST + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) pop hl + ld de, MEM0 + ret -__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 +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 -__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 +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 - ld e, (hl) ; DE -> block->next->length - inc hl - ld d, (hl) - inc hl + 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 - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length + ENDP + - ld b, h - ld c, l ; BC = Total Length +#line 58 "param0.bas" +#line 1 "printstr.asm" - 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 @@ -1546,193 +1777,9 @@ __PRINT_STR: ENDP -#line 56 "param0.bas" +#line 59 "param0.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. - - - - - - ; --------------------------------------------------------------------- - ; 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/library-asm/alloc.asm" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/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 "strcat.asm" #line 1 "strlen.asm" ; Returns len if a string ; If a string is NULL, its len is also 0 @@ -1875,53 +1922,6 @@ __STRCATEND: ENDP -#line 57 "param0.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 - 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 60 "param0.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/param1.asm b/tests/functional/param1.asm index 8feb930bb..4b56ce66b 100644 --- a/tests/functional/param1.asm +++ b/tests/functional/param1.asm @@ -60,7 +60,10 @@ _test__leave: __LABEL0: DEFW 0001h DEFB 61h -#line 1 "printstr.asm" +#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 @@ -1166,7 +1169,15 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#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" @@ -1538,7 +1549,7 @@ __PRINT_STR: ENDP -#line 48 "param1.bas" +#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 @@ -2024,17 +2035,6 @@ __STORE_STR: pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret -#line 49 "param1.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 50 "param1.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/param2.asm b/tests/functional/param2.asm index 65824ed7e..4b71edcb4 100644 --- a/tests/functional/param2.asm +++ b/tests/functional/param2.asm @@ -62,1438 +62,1669 @@ _test__leave: ex (sp), hl exx ret -__LABEL1: - DEFW 0001h - DEFB 41h __LABEL0: DEFW 0001h DEFB 48h -#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 "sposn.asm" - ; Printing positioning library. - PROC - LOCAL ECHO_E +__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) +; 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 -__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 - + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. -__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 +; 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)| + ; +----------------+ - ECHO_E EQU 23682 - MAXX EQU ECHO_E ; Max X position + 1 - MAXY EQU MAXX + 1 ; Max Y position + 1 + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). - S_POSN EQU 23688 - POSX EQU S_POSN ; Current POS X - POSY EQU S_POSN + 1 ; Current POS Y - ENDP + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. -#line 6 "print.asm" -#line 1 "cls.asm" - ; JUMPS directly to spectrum CLS - ; This routine does not clear lower screen + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - ;CLS EQU 0DAFh +#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 - ; Our faster implementation + ; ----- 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)| + ; +----------------+ -CLS: - PROC + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). - 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 + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - ; Now clear attributes + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - 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" + ; --------------------------------------------------------------------- + ; __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 -#line 1 "error.asm" - ; Simple error control routines -; vim:ts=4:et: + ; --------------------------------------------------------------------- + ; __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 - ERR_NR EQU 23610 ; Error code system variable + 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 - ; Error code definitions (as in ZX spectrum manual) + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block -; Set error code with: - ; ld a, ERROR_CODE - ; ld (ERR_NR), a + 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 - 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_InvalidFileName EQU 14 - ERROR_InvalidColour EQU 19 - ERROR_BreakIntoProgram EQU 20 - ERROR_TapeLoadingErr EQU 26 + 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 - ; 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) + ENDP -__IN_SCREEN_ERR: -__OUT_OF_SCREEN_ERR: - ; Jumps here if out of screen - ld a, ERROR_OutOfScreen - jp __STOP ; Saves error code and exits +#line 69 "free.asm" - ENDP -#line 8 "print.asm" -#line 1 "table_jump.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 + ; --------------------------------------------------------------------- -JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A - add a, a +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC -JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE - ld e, a - ld d, 0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN -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) + ld a, h + or l + ret z ; Return if NULL pointer -#line 9 "print.asm" -#line 1 "ink.asm" - ; Sets ink color in ATTR_P permanently -; Parameter: Paper color in A register + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -#line 1 "const.asm" - ; Global constants + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start - 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 +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr -#line 5 "ink.asm" + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next -INK: - PROC - LOCAL __SET_INK - LOCAL __SET_INK2 + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous - ld de, ATTR_P + 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 -__SET_INK: - cp 8 - jr nz, __SET_INK2 + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next - 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 +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl -__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 + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC - ; Sets the INK color passed in A register in the ATTR_T variable -INK_TMP: - ld de, ATTR_T - jp __SET_INK + 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 - ENDP + call __MEM_JOIN_TEST + pop hl -#line 10 "print.asm" -#line 1 "paper.asm" - ; Sets paper color in ATTR_P permanently -; Parameter: Paper color in A register +__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 -PAPER: - PROC - LOCAL __SET_PAPER - LOCAL __SET_PAPER2 - - ld de, ATTR_P + ld b, h + ld c, l ; BC = Total Length -__SET_PAPER: - cp 8 - jr nz, __SET_PAPER2 - inc de - ld a, (de) - or 038h - ld (de), a - ret + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next - ; Another entry. This will set the paper color at location pointer by DE -__SET_PAPER2: - and 7 ; # Remove - rlca - rlca - rlca ; a *= 8 + 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 - 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 + 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) +; 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 - ; 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 + ; ----- 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)| + ; +----------------+ -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 + ; When 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). - rrca - ld b, a ; Saves the color - ld a, (de) - and 07Fh ; Clears previous value - or b - ld (de), a - ret + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - ; Sets the FLASH flag passed in A register in the ATTR_T variable -FLASH_TMP: - ld de, ATTR_T - jr __SET_FLASH + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. -#line 12 "print.asm" -#line 1 "bright.asm" - ; Sets bright flag in ATTR_P permanently -; Parameter: Paper color in A register +#line 1 "error.asm" + ; Simple error control routines +; vim:ts=4:et: + ERR_NR EQU 23610 ; Error code system variable -BRIGHT: - ld de, ATTR_P + ; Error code definitions (as in ZX spectrum manual) -__SET_BRIGHT: - ; Another entry. This will set the bright flag at location pointer by DE - and 1 ; # Convert to 0/1 +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - rrca - rrca - ld b, a ; Saves the color - ld a, (de) - and 0BFh ; Clears previous value - or b - ld (de), a - ret + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - ; 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" + ; 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 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.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) + ; --------------------------------------------------------------------- -COPY_ATTR: - ; Just copies current permanent attribs to temporal attribs - ; and sets print mode - PROC +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC - LOCAL INVERSE1 - LOCAL __REFRESH_TMP + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - INVERSE1 EQU 02Fh + TEMP EQU TEMP0 + 1 - ld hl, (ATTR_P) - ld (ATTR_T), hl + ld hl, 0 + ld (TEMP), hl - ld hl, FLAGS2 - call __REFRESH_TMP - - ld hl, P_FLAG - call __REFRESH_TMP +__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" + ret z ; NULL +#line 113 "/home/boriel/Documents/src/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 -__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + 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 - LOCAL TABLE - LOCAL CONT2 + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + + ENDP - 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 +#line 2 "loadstr.asm" - ld hl, TABLE - add hl, bc - ld a, (hl) - ld (PRINT_MODE), a + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL - 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 +__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 -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL -__REFRESH_TMP: - ld a, (hl) - and 10101010b - ld c, a - rra - or c - ld (hl), a - ret + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(a$) - ENDP + inc bc + inc bc ; BC = LEN(a$) + 2 (two bytes for length) -#line 4 "over.asm" + 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) -OVER: - PROC + 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 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 - ld c, a ; saves it for later - and 2 - ld hl, FLAGS2 - res 1, (HL) - or (hl) - ld (hl), a +#line 1 "sposn.asm" + ; Printing positioning library. + PROC + LOCAL ECHO_E - ld a, c ; Recovers previous value - and 1 ; # Convert to 0/1 - add a, a; # Shift left 1 bit for permanent +__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 + - ld hl, P_FLAG - res 1, (hl) - or (hl) - ld (hl), a - 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 - ; 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 + ECHO_E EQU 23682 + MAXX EQU ECHO_E ; Max X position + 1 + MAXY EQU MAXX + 1 ; Max Y position + 1 - ENDP + S_POSN EQU 23688 + POSX EQU S_POSN ; Current POS X + POSY EQU S_POSN + 1 ; Current POS Y -#line 14 "print.asm" -#line 1 "inverse.asm" - ; Sets INVERSE flag in P_FLAG permanently -; Parameter: INVERSE flag in bit 0 of A register + ENDP +#line 6 "print.asm" +#line 1 "cls.asm" + ; JUMPS directly to spectrum CLS + ; This routine does not clear lower screen + ;CLS EQU 0DAFh -INVERSE: - PROC + ; Our faster implementation - 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 +CLS: + PROC -#line 15 "print.asm" -#line 1 "bold.asm" - ; Sets BOLD flag in P_FLAG permanently -; Parameter: BOLD flag in bit 0 of A register + 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 -BOLD: - PROC + ; Now clear attributes - and 1 - rlca - rlca - rlca - ld hl, FLAGS2 - res 3, (HL) - or (hl) + ld a, (ATTR_P) ld (hl), a + ld bc, 767 + ldir 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 + 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 16 "print.asm" -#line 1 "italic.asm" - ; Sets ITALIC flag in P_FLAG permanently -; Parameter: ITALIC flag in bit 0 of A register +#line 7 "print.asm" +#line 1 "in_screen.asm" -ITALIC: - PROC - and 1 - rrca - rrca - rrca - ld hl, FLAGS2 - res 5, (HL) - or (hl) - ld (hl), a - ret +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) - ; 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 + PROC + LOCAL __IN_SCREEN_ERR - ENDP + ld hl, MAXX + ld a, e + cp (hl) + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range -#line 17 "print.asm" - -#line 1 "attr.asm" - ; Attribute routines -; vim:ts=4:et:sw: + 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 -__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 +#line 1 "const.asm" + ; Global constants - 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 + 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 - ld de, (SCREEN_ADDR) ; Adds the screen address - add hl, de - - ; Return current screen address in HL - ret +#line 5 "ink.asm" +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 - ; 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: + ld de, ATTR_P - ; Checks for valid coords - call __IN_SCREEN - ret nc +__SET_INK: + cp 8 + jr nz, __SET_INK2 -__SET_ATTR: - ; Internal __FASTCALL__ Entry used by printing routines - PROC + 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 - call __ATTR_ADDR - ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T +__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 - ld a, d - and (hl) - ld c, a ; C = current screen color, masked + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK - ld a, d - cpl ; Negate mask - and e ; Mask current attributes - or c ; Mix them - ld (hl), a ; Store result in screen - - ret - - ENDP + ENDP +#line 10 "print.asm" +#line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register -#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. +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + + ld de, ATTR_P -__PRINT_INIT: ; To be called before program starts (initializes library) - PROC +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret - ld hl, __PRINT_START - ld (PRINT_JUMP_STATE), hl + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 - ld hl, 1821h - ld (MAXX), hl ; Sets current maxX and maxY + 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 - xor a - ld (FLAGS2), 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 -__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 +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 - PRINT_JUMP_STATE EQU __PRINT_JUMP + 1 + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret -__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 + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld de, ATTR_T + jr __SET_FLASH - exx ; Switch to alternative registers - ex af, af' ; Saves a value (char to print) for later +#line 12 "print.asm" +#line 1 "bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register - 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 +BRIGHT: + ld de, ATTR_P - ld a, c ; Recovers it - and 07h ; MOD 7 ; y1 - rrca - rrca - rrca +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 - or e - ld e, a - add hl, de ; HL = Screen address + DE - ex de, hl ; DE = Screen address - - ex af, af' + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret - cp 80h ; Is it an UDG or a ? - jp c, __SRCADDR - cp 90h - jp nc, __PRINT_UDG + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT - ; Print a 8 bit pattern (80h to 8Fh) +#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" - 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 +#line 4 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" -__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 ; +COPY_ATTR: + ; Just copies current permanent attribs to temporal attribs + ; and sets print mode + PROC -INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 - nop ; 2F -> CPL -> INVERSE 1 + LOCAL INVERSE1 + LOCAL __REFRESH_TMP - ld (hl), a + INVERSE1 EQU 02Fh - inc de - inc h ; Next line - djnz __PRCHAR + ld hl, (ATTR_P) + ld (ATTR_T), hl - 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 + ld hl, FLAGS2 + call __REFRESH_TMP + + ld hl, P_FLAG + call __REFRESH_TMP -__PRINT_CONT: - call __SAVE_S_POSN -__PRINT_CONT2: - exx - ret +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) - ; ------------- SPECIAL CHARS (< 32) ----------------- -__PRINT_SPECIAL: ; Jumps here if it is a special char - exx - ld hl, __PRINT_TABLE - jp JUMP_HL_PLUS_2A + 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) -PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence - exx + ld c, a + ld b, 0 -__PRINT_0Dh: ; Called WHEN printing CHR$(13) - call __LOAD_S_POSN + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a -__PRINT_EOL1: ; Another entry called from PRINT when next line required - ld e, 0 + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 -__PRINT_EOL2: - ld a, d - inc a +CONT2: + ld (INVERSE_MODE), a + ret -__PRINT_AT1_END: - ld hl, (MAXY) - cp l - jr c, __PRINT_EOL_END ; Carry if (MAXY) < d - xor a +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE -__PRINT_EOL_END: - ld d, a +#line 65 "/home/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" -__PRINT_AT2_END: - call __SAVE_S_POSN - exx - ret +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret -__PRINT_COM: - exx - push hl - push de - push bc - call PRINT_COMMA - pop bc - pop de - pop hl - ret + ENDP -__PRINT_TAB: - ld hl, __PRINT_TAB1 - jp __PRINT_SET_STATE +#line 4 "over.asm" -__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 +OVER: + PROC -__PRINT_NOP: -__PRINT_RESTART: - ld hl, __PRINT_START - jp __PRINT_SET_STATE + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a -__PRINT_AT: - ld hl, __PRINT_AT1 + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent -__PRINT_SET_STATE: - ld (PRINT_JUMP_STATE), hl ; Saves next entry call - exx - ret + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + 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 + ; 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 -__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 + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE -__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 + ENDP -__PRINT_INK: - ld hl, __PRINT_INK2 - jp __PRINT_SET_STATE +#line 14 "print.asm" +#line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register -__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 +INVERSE: + PROC -__PRINT_FLA2: - exx - call FLASH_TMP - jp __PRINT_RESTART + 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 -__PRINT_BRI: - ld hl, __PRINT_BRI2 - jp __PRINT_SET_STATE + ; 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 -__PRINT_BRI2: - exx - call BRIGHT_TMP - jp __PRINT_RESTART + ENDP -__PRINT_INV: - ld hl, __PRINT_INV2 - jp __PRINT_SET_STATE +#line 15 "print.asm" +#line 1 "bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register -__PRINT_INV2: - exx - call INVERSE_TMP - jp __PRINT_RESTART -__PRINT_OVR: - ld hl, __PRINT_OVR2 - jp __PRINT_SET_STATE +BOLD: + PROC -__PRINT_OVR2: - exx - call OVER_TMP - jp __PRINT_RESTART + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret -__PRINT_BOLD: - ld hl, __PRINT_BOLD2 - jp __PRINT_SET_STATE + ; 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 -__PRINT_BOLD2: - exx - call BOLD_TMP - jp __PRINT_RESTART + ENDP -__PRINT_ITA: - ld hl, __PRINT_ITA2 - jp __PRINT_SET_STATE +#line 16 "print.asm" +#line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register -__PRINT_ITA2: - exx - call ITALIC_TMP - jp __PRINT_RESTART +ITALIC: + PROC -__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 - + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + 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 + ; 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 -PRINT_COMMA: - call __LOAD_S_POSN - ld a, e - and 16 - add a, 16 + ENDP -PRINT_TAB: - PROC - LOCAL LOOP, CONTINUE +#line 17 "print.asm" - 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 +#line 1 "attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: -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" +__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 -#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 + ld de, (SCREEN_ADDR) ; Adds the screen address + add hl, de + + ; Return current screen address in HL + ret - ; ----- 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)| - ; +----------------+ + ; 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 - ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the - ; previous, or to the next (or both) they will be converted into a single - ; block (so defragmented). +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + PROC + call __ATTR_ADDR + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T - ; MEMORY MANAGER - ; - ; This library must be initialized calling __MEM_INIT with - ; HL = BLOCK Start & DE = Length. + ld a, d + and (hl) + ld c, a ; C = current screen color, masked - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. + 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 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. +#line 19 "print.asm" -; 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)| - ; +----------------+ + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. - ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the - ; previous, or to the next (or both) they will be converted into a single - ; block (so defragmented). +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl - ; MEMORY MANAGER - ; - ; This library must be initialized calling __MEM_INIT with - ; HL = BLOCK Start & DE = Length. + ld hl, 1821h + ld (MAXX), hl ; Sets current maxX and maxY - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. + xor a + ld (FLAGS2), a + ret +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A - ; --------------------------------------------------------------------- - ; __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 + LOCAL PO_GR_1 - ; --------------------------------------------------------------------- - ; __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 + LOCAL __PRCHAR + LOCAL __PRINT_CONT + LOCAL __PRINT_CONT2 + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START - 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 + 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 - 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 + inc de + inc h ; Next line + djnz __PRCHAR - ld (hl), c - inc hl - ld (hl), b - inc hl ; Pointer to next block + 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 - 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 +__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 - ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) - inc hl - ld (hl), a +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART - ld a, 201 - ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again - ret +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART - ENDP +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE -#line 69 "free.asm" +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART - ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory - ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done - ; --------------------------------------------------------------------- +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE -MEM_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified - PROC +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE - ld a, h - or l - ret z ; Return if NULL pointer +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART - ld a, h ; HL == NULL? - or l - jp z, __MEM_LINK_PREV; if so, link with previous +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE - 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 +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART - ;------ 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 +__BOLD: push hl - dec hl - - ld (hl), c + ld hl, MEM0 + ld b, 8 +__BOLD_LOOP: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a inc hl - ld (hl), b ; (DE) <- BC + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + - ld h, b ; HL <- BC (Free block ptr) - ld l, c - inc hl ; Skip block length (2 bytes) +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) inc hl - ld (hl), e ; Block->next = DE + srl (hl) inc hl - ld (hl), d - ; --- LINKED ; HL = &(BC->next) + 2 - - call __MEM_JOIN_TEST + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) pop hl + ld de, MEM0 + ret -__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 +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 -__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 +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 - ld e, (hl) ; DE -> block->next->length - inc hl - ld d, (hl) - inc hl + 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 - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length + ENDP + - ld b, h - ld c, l ; BC = Total Length +#line 58 "param2.bas" +#line 1 "printstr.asm" - 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 @@ -1546,193 +1777,9 @@ __PRINT_STR: ENDP -#line 56 "param2.bas" +#line 59 "param2.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. - - - - - - ; --------------------------------------------------------------------- - ; 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/library-asm/alloc.asm" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/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 "strcat.asm" #line 1 "strlen.asm" ; Returns len if a string ; If a string is NULL, its len is also 0 @@ -1875,53 +1922,6 @@ __STRCATEND: ENDP -#line 57 "param2.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 - 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 60 "param2.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/paramstr3.asm b/tests/functional/paramstr3.asm index 88301a7a5..33c21952d 100644 --- a/tests/functional/paramstr3.asm +++ b/tests/functional/paramstr3.asm @@ -517,9 +517,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/paramstr4.asm b/tests/functional/paramstr4.asm index ec783e4fc..44d681913 100644 --- a/tests/functional/paramstr4.asm +++ b/tests/functional/paramstr4.asm @@ -541,9 +541,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/paramstr5.asm b/tests/functional/paramstr5.asm index b68794996..937d6dae9 100644 --- a/tests/functional/paramstr5.asm +++ b/tests/functional/paramstr5.asm @@ -541,9 +541,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/plot.asm b/tests/functional/plot.asm index aff43e140..4766d884f 100644 --- a/tests/functional/plot.asm +++ b/tests/functional/plot.asm @@ -58,6 +58,115 @@ __END_PROGRAM: ret __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 + + 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, __NEG32 ; Negates DEHL + + ret + + 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 @@ -298,115 +407,6 @@ __PLOT_ERR: PIXEL_ADDR EQU 22ACh COORDS EQU 5C7Dh ENDP -#line 50 "plot.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 - - 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, __NEG32 ; Negates DEHL - - ret - - ENDP - - -__FTOU8: ; Converts float in C ED LH to Unsigned byte in A - call __FTOU32REG - ld a, l - ret - #line 51 "plot.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/print.asm b/tests/functional/print.asm index 57cd772d3..89465b83e 100644 --- a/tests/functional/print.asm +++ b/tests/functional/print.asm @@ -64,74 +64,94 @@ __CALL_BACK__: __LABEL0: DEFW 0001h DEFB 33h -#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 +#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 - ; ----- IMPLEMENTATION NOTES ------ - ; The heap is implemented as a linked list of free blocks. +#line 1 "sposn.asm" + ; Printing positioning library. + PROC + LOCAL ECHO_E -; 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)| - ; +----------------+ +__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 - ; When 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). + 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 - ; MEMORY MANAGER - ; - ; This library must be initialized calling __MEM_INIT with - ; HL = BLOCK Start & DE = Length. + ENDP - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. +#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 @@ -173,1458 +193,1458 @@ __ERROR_CODE: __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 +#line 3 "in_screen.asm" - ; ----- IMPLEMENTATION NOTES ------ - ; The heap is implemented as a linked list of free blocks. +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits (MAXX, MAXY) -; 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)| - ; +----------------+ + 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 - ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the - ; previous, or to the next (or both) they will be converted into a single - ; block (so defragmented). + 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 - ; MEMORY MANAGER - ; - ; This library must be initialized calling __MEM_INIT with - ; HL = BLOCK Start & DE = Length. + ENDP +#line 8 "print.asm" +#line 1 "table_jump.asm" - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. +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 - ; --------------------------------------------------------------------- - ; __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 +#line 1 "const.asm" + ; Global constants - ; --------------------------------------------------------------------- - ; __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 + 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 - 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 +#line 5 "ink.asm" - xor a - ld (hl), a - inc hl - ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 - inc hl +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 - ld b, h - ld c, l - inc bc - inc bc ; BC = starts of next block + ld de, ATTR_P - ld (hl), c - inc hl - ld (hl), b - inc hl ; Pointer to next block +__SET_INK: + cp 8 + jr nz, __SET_INK2 - 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 + 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 - ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) - inc hl - ld (hl), a +__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 - ld a, 201 - ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again - ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK - ENDP + ENDP -#line 70 "alloc.asm" +#line 10 "print.asm" +#line 1 "paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register - ; --------------------------------------------------------------------- - ; 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 +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + + ld de, ATTR_P - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret - TEMP EQU TEMP0 + 1 + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 - ld hl, 0 - ld (TEMP), hl + 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 -__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" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/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 + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP - ld (hl), e - inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret +#line 11 "print.asm" +#line 1 "flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register -__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 +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 -#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 + rrca + ld b, a ; Saves the color + ld a, (de) + and 07Fh ; Clears previous value + or b + ld (de), a + ret -__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 + ; 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 -#line 3 "strcat.asm" -__ADDSTR: ; Implements c$ = a$ + b$ - ; hl = &a$, de = &b$ (pointers) +BRIGHT: + ld de, ATTR_P -__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 +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + and 1 ; # Convert to 0/1 - 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$ + rrca + rrca + ld b, a ; Saves the color + ld a, (de) + and 0BFh ; Clears previous value + or b + ld (de), a + ret - inc bc - inc bc ; +2 bytes to store length - ex de, hl - push hl - call __STRLEN - ; HL = len(b$) + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld de, ATTR_T + jr __SET_BRIGHT - add hl, bc ; Total str length => 2 + len(a$) + len(b$) +#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" - 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$ +#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - exx - pop de ; D'E' = b$ - exx - pop bc ; LEN(a$) - ld a, d - or e - ret z ; If no memory: RETURN +COPY_ATTR: + ; Just copies current permanent attribs to temporal attribs + ; and sets print mode + PROC -__STR_CONT: - push de ; Address of c$ + LOCAL INVERSE1 + LOCAL __REFRESH_TMP - ld a, h - or l - jr nz, __STR_CONT1 ; If len(a$) != 0 do copy + INVERSE1 EQU 02Fh - ; 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 + ld hl, (ATTR_P) + ld (ATTR_T), hl -__STR_CONT1: ; Copies a$ (HL) into c$ (DE) - inc bc - inc bc ; BC = BC + 2 - ldir ; MEMCOPY: c$ = a$ - pop hl ; HL = c$ + ld hl, FLAGS2 + call __REFRESH_TMP + + ld hl, P_FLAG + call __REFRESH_TMP - exx - push de ; Recovers b$; A ex hl,hl' would be very handy - exx - pop de ; DE = b$ +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) -__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 + LOCAL TABLE + LOCAL CONT2 - 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 + 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) - ex de, hl ; DE = end of string (Begin of copy addr) - ld c, (hl) - inc hl - ld b, (hl) ; BC = len(b$) + ld c, a + ld b, 0 - ld a, b - or c - jr z, __STRCATEND; Return if len(b$) == 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a - push bc ; Save LEN(b$) - inc hl ; Skip 2nd byte of len(b$) - ldir ; Concatenate b$ + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 - 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 +CONT2: + ld (INVERSE_MODE), a + ret - ld (hl), e ; Updates new LEN and return - inc hl - ld (hl), d - dec hl - ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE -__STRCATEND: - pop hl ; Removes Len(a$) - pop hl ; Restores original HL, so HL = a$ - ret +#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" - ENDP +__REFRESH_TMP: + ld a, (hl) + and 10101010b + ld c, a + rra + or c + ld (hl), a + ret -#line 52 "print.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 + ENDP -#line 1 "sposn.asm" - ; Printing positioning library. - PROC - LOCAL ECHO_E +#line 4 "over.asm" -__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 +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a - ECHO_E EQU 23682 - MAXX EQU ECHO_E ; Max X position + 1 - MAXY EQU MAXX + 1 ; Max Y position + 1 + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent - S_POSN EQU 23688 - POSX EQU S_POSN ; Current POS X - POSY EQU S_POSN + 1 ; Current POS Y + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret - ENDP + ; 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 -#line 6 "print.asm" -#line 1 "cls.asm" - ; JUMPS directly to spectrum CLS - ; This routine does not clear lower screen + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE - ;CLS EQU 0DAFh + ENDP - ; Our faster implementation +#line 14 "print.asm" +#line 1 "inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register -CLS: +INVERSE: PROC - LOCAL COORDS - LOCAL __CLS_SCR - LOCAL ATTR_P - LOCAL SCREEN + 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 - 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 + ; 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 - ; Now clear attributes + ENDP - ld a, (ATTR_P) +#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 - 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 + ; 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 - 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 16 "print.asm" +#line 1 "italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register -__IN_SCREEN: - ; Returns NO carry if current coords (D, E) - ; are OUT of the screen limits (MAXX, MAXY) +ITALIC: 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) + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret -__IN_SCREEN_ERR: -__OUT_OF_SCREEN_ERR: - ; Jumps here if out of screen - ld a, ERROR_OutOfScreen - jp __STOP ; Saves error code and exits + ; 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 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 +#line 17 "print.asm" -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 1 "attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: -#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 +__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 - 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 + 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 -__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 + ld de, (SCREEN_ADDR) ; Adds the screen address + add hl, de + + ; Return current screen address in HL + ret - ; Sets the INK color passed in A register in the ATTR_T variable -INK_TMP: - ld de, ATTR_T - jp __SET_INK - ENDP + ; 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: -#line 10 "print.asm" -#line 1 "paper.asm" - ; Sets paper color in ATTR_P permanently -; Parameter: Paper color in A register + ; 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 -PAPER: - PROC - LOCAL __SET_PAPER - LOCAL __SET_PAPER2 - - ld de, ATTR_P + ld a, d + and (hl) + ld c, a ; C = current screen color, masked -__SET_PAPER: - cp 8 - jr nz, __SET_PAPER2 - inc de - ld a, (de) - or 038h - ld (de), a - ret + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + + ret + + ENDP - ; 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 +#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. - ; 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 +__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 -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 + xor a + ld (FLAGS2), a - rrca - ld b, a ; Saves the color - ld a, (de) - and 07Fh ; Clears previous value - or b - ld (de), a - ret + ret - ; Sets the FLASH flag passed in A register in the ATTR_T variable -FLASH_TMP: - ld de, ATTR_T - jr __SET_FLASH +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A -#line 12 "print.asm" -#line 1 "bright.asm" - ; Sets bright flag in ATTR_P permanently -; Parameter: Paper color in A register + 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 -BRIGHT: - ld de, ATTR_P +__PRINT_JUMP: + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively -__SET_BRIGHT: - ; Another entry. This will set the bright flag at location pointer by DE - and 1 ; # Convert to 0/1 +__PRINT_START: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones - rrca - rrca - ld b, a ; Saves the color - ld a, (de) - and 0BFh ; Clears previous value - or b - ld (de), a - ret + exx ; Switch to alternative registers + ex af, af' ; Saves a value (char to print) for later + call __LOAD_S_POSN - ; Sets the BRIGHT flag passed in A register in the ATTR_T variable -BRIGHT_TMP: - ld de, ATTR_T - jr __SET_BRIGHT + ; At this point we have the new coord + ld hl, (SCREEN_ADDR) -#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" + 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 -#line 4 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" + 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 -COPY_ATTR: - ; Just copies current permanent attribs to temporal attribs - ; and sets print mode - PROC + ; Print a 8 bit pattern (80h to 8Fh) - LOCAL INVERSE1 - LOCAL __REFRESH_TMP + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH - INVERSE1 EQU 02Fh + PO_GR_1 EQU 0B38h - ld hl, (ATTR_P) - ld (ATTR_T), hl +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jp __PRGRAPH0 - ld hl, FLAGS2 - call __REFRESH_TMP - - ld hl, P_FLAG - call __REFRESH_TMP + __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 -__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) +__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 ; - LOCAL TABLE - LOCAL CONT2 +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 - 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 (hl), a - ld c, a - ld b, 0 + inc de + inc h ; Next line + djnz __PRCHAR - ld hl, TABLE - add hl, bc - ld a, (hl) - ld (PRINT_MODE), a + 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 - ld hl, (P_FLAG) - xor a ; NOP -> INVERSE0 - bit 2, l - jr z, CONT2 - ld a, INVERSE1 ; CPL -> INVERSE1 +__PRINT_CONT: + call __SAVE_S_POSN -CONT2: - ld (INVERSE_MODE), a - ret +__PRINT_CONT2: + exx + ret -TABLE: - nop ; NORMAL MODE - xor (hl) ; OVER 1 MODE - and (hl) ; OVER 2 MODE - or (hl) ; OVER 3 MODE + ; ------------- SPECIAL CHARS (< 32) ----------------- -#line 65 "/Users/boriel/Documents/src/zxbasic/library-asm/copy_attr.asm" +__PRINT_SPECIAL: ; Jumps here if it is a special char + exx + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A -__REFRESH_TMP: - ld a, (hl) - and 10101010b - ld c, a - rra - or c - ld (hl), a - ret - ENDP +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx -#line 4 "over.asm" +__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 -OVER: - PROC +__PRINT_EOL2: + ld a, d + inc a - ld c, a ; saves it for later - and 2 - ld hl, FLAGS2 - res 1, (HL) - or (hl) - ld (hl), a +__PRINT_AT1_END: + ld hl, (MAXY) + cp l + jr c, __PRINT_EOL_END ; Carry if (MAXY) < d + xor a - ld a, c ; Recovers previous value - and 1 ; # Convert to 0/1 - add a, a; # Shift left 1 bit for permanent +__PRINT_EOL_END: + ld d, a - ld hl, P_FLAG - res 1, (hl) - or (hl) - ld (hl), a - ret +__PRINT_AT2_END: + call __SAVE_S_POSN + exx + 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 +__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 -INVERSE: - PROC +__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 - 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 +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + jp __PRINT_SET_STATE - ; 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 +__PRINT_AT: + ld hl, __PRINT_AT1 - ENDP +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret -#line 15 "print.asm" -#line 1 "bold.asm" - ; Sets BOLD flag in P_FLAG permanently -; Parameter: BOLD flag in bit 0 of A register +__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 -BOLD: - PROC +__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 - and 1 - rlca - rlca - rlca - ld hl, FLAGS2 - res 3, (HL) - or (hl) - ld (hl), a - ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jp __PRINT_SET_STATE - ; 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 +__PRINT_INK2: + exx + call INK_TMP + jp __PRINT_RESTART - ENDP +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jp __PRINT_SET_STATE + +__PRINT_PAP2: + exx + call PAPER_TMP + jp __PRINT_RESTART -#line 16 "print.asm" -#line 1 "italic.asm" - ; Sets ITALIC flag in P_FLAG permanently -; Parameter: ITALIC flag in bit 0 of A register +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jp __PRINT_SET_STATE +__PRINT_FLA2: + exx + call FLASH_TMP + jp __PRINT_RESTART -ITALIC: - PROC +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jp __PRINT_SET_STATE - and 1 - rrca - rrca - rrca - ld hl, FLAGS2 - res 5, (HL) - or (hl) - ld (hl), a - ret +__PRINT_BRI2: + exx + call BRIGHT_TMP + jp __PRINT_RESTART - ; 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 +__PRINT_INV: + ld hl, __PRINT_INV2 + jp __PRINT_SET_STATE - ENDP +__PRINT_INV2: + exx + call INVERSE_TMP + jp __PRINT_RESTART -#line 17 "print.asm" +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE -#line 1 "attr.asm" - ; Attribute routines -; vim:ts=4:et:sw: +__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 + -__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 +__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 - ; 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: +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE - ; Checks for valid coords - call __IN_SCREEN - ret nc + 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 -__SET_ATTR: - ; Internal __FASTCALL__ Entry used by printing routines - PROC +PRINT_AT: ; CHanges cursor to ROW, COL + ; COL in A register + ; ROW in stack - call __ATTR_ADDR - ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl - ld a, d - and (hl) - ld c, a ; C = current screen color, masked + call __IN_SCREEN + ret nc ; Return if out of screen - ld a, d - cpl ; Negate mask - and e ; Mask current attributes - or c ; Mix them - ld (hl), a ; Store result in screen - - ret - - ENDP + 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 -#line 19 "print.asm" + ENDP + - ; Putting a comment starting with @INIT
- ; will make the compiler to add a CALL to
- ; It is useful for initialization routines. +#line 52 "print.bas" +#line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves + ; 3 bytes -__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 +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 53 "print.bas" +#line 1 "printf.asm" +#line 1 "printstr.asm" - xor a - ld (FLAGS2), a - ret +#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 -__PRINTCHAR: ; Print character store in accumulator (A register) - ; Modifies H'L', B'C', A'F', D'E', A + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. - LOCAL PO_GR_1 +; 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)| + ; +----------------+ - 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 + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). -__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 + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - exx ; Switch to alternative registers - ex af, af' ; Saves a value (char to print) for later + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - call __LOAD_S_POSN +#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 - ; At this point we have the new coord - ld hl, (SCREEN_ADDR) + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. - ld a, d - ld c, a ; Saves it for later - - and 0F8h ; Masks 3 lower bit ; zy - ld d, a +; 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)| + ; +----------------+ - 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' + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). - cp 80h ; Is it an UDG or a ? - jp c, __SRCADDR - cp 90h - jp nc, __PRINT_UDG + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - ; Print a 8 bit pattern (80h to 8Fh) + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - 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) + ; --------------------------------------------------------------------- + ; __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 -__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 + ; --------------------------------------------------------------------- + ; __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 -__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 + 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 -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 ; + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl -INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 - nop ; 2F -> CPL -> INVERSE 1 + 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 - inc de - inc h ; Next line - djnz __PRCHAR + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret - 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 + ENDP -__PRINT_CONT: - call __SAVE_S_POSN +#line 69 "free.asm" -__PRINT_CONT2: - exx - ret + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- - ; ------------- SPECIAL CHARS (< 32) ----------------- +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC -__PRINT_SPECIAL: ; Jumps here if it is a special char - exx - ld hl, __PRINT_TABLE - jp JUMP_HL_PLUS_2A + 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 -PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence - exx + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__PRINT_0Dh: ; Called WHEN printing CHR$(13) - call __LOAD_S_POSN + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start -__PRINT_EOL1: ; Another entry called from PRINT when next line required - ld e, 0 +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr -__PRINT_EOL2: - ld a, d - inc a + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next -__PRINT_AT1_END: - ld hl, (MAXY) - cp l - jr c, __PRINT_EOL_END ; Carry if (MAXY) < d - xor a + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous -__PRINT_EOL_END: - ld d, a + 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 -__PRINT_AT2_END: - call __SAVE_S_POSN - exx - ret + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next -__PRINT_COM: - exx +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl push hl - push de - push bc - call PRINT_COMMA - pop bc - pop de + 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 - ret -__PRINT_TAB: - ld hl, __PRINT_TAB1 - jp __PRINT_SET_STATE +__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 -__PRINT_TAB1: - ld (MEM0), a - ld hl, __PRINT_TAB2 - ld (PRINT_JUMP_STATE), hl +__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 -__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 + ENDP -__PRINT_NOP: -__PRINT_RESTART: - ld hl, __PRINT_START - jp __PRINT_SET_STATE +#line 5 "printstr.asm" -__PRINT_AT: - ld hl, __PRINT_AT1 + ; PRINT command routine + ; Prints string pointed by HL -__PRINT_SET_STATE: - ld (PRINT_JUMP_STATE), hl ; Saves next entry call - exx - ret +PRINT_STR: +__PRINTSTR: ; __FASTCALL__ Entry to print_string + PROC + LOCAL __PRINT_STR_LOOP + LOCAL __PRINT_STR_END -__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 + ld d, a ; Saves A reg (Flag) for later -__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 + ld a, h + or l + ret z ; Return if the pointer is NULL -__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 + push hl -__PRINT_INK: - ld hl, __PRINT_INK2 - jp __PRINT_SET_STATE + ld c, (hl) + inc hl + ld b, (hl) + inc hl ; BC = LEN(a$); HL = &a$ -__PRINT_INK2: - exx - call INK_TMP - jp __PRINT_RESTART +__PRINT_STR_LOOP: + ld a, b + or c + jr z, __PRINT_STR_END ; END if BC (counter = 0) -__PRINT_PAP: - ld hl, __PRINT_PAP2 - jp __PRINT_SET_STATE - -__PRINT_PAP2: - exx - call PAPER_TMP - jp __PRINT_RESTART + ld a, (hl) + call __PRINTCHAR + inc hl + dec bc + jp __PRINT_STR_LOOP -__PRINT_FLA: - ld hl, __PRINT_FLA2 - jp __PRINT_SET_STATE +__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_FLA2: - exx - call FLASH_TMP - jp __PRINT_RESTART +__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 -__PRINT_BRI: - ld hl, __PRINT_BRI2 - jp __PRINT_SET_STATE + ENDP -__PRINT_BRI2: - exx - call BRIGHT_TMP - jp __PRINT_RESTART +#line 2 "printf.asm" +#line 1 "stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- -__PRINT_INV: - ld hl, __PRINT_INV2 - jp __PRINT_SET_STATE -__PRINT_INV2: - exx - call INVERSE_TMP - jp __PRINT_RESTART + __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) -__PRINT_OVR: - ld hl, __PRINT_OVR2 - jp __PRINT_SET_STATE +__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 -__PRINT_OVR2: - exx - call OVER_TMP - jp __PRINT_RESTART + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr -__PRINT_BOLD: - ld hl, __PRINT_BOLD2 - jp __PRINT_SET_STATE + pop af + pop de + pop bc -__PRINT_BOLD2: - exx - call BOLD_TMP - jp __PRINT_RESTART + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + + jp __FPSTACK_PUSH -__PRINT_ITA: - ld hl, __PRINT_ITA2 - jp __PRINT_SET_STATE -__PRINT_ITA2: - exx - call ITALIC_TMP - jp __PRINT_RESTART +__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" -__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 - +__PRINTF: ; Prints a Fixed point Number stored in C ED LH + PROC -__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 + LOCAL RECLAIM2 + LOCAL STK_END + STK_END EQU 5C65h -PRINT_COMMA: - call __LOAD_S_POSN - ld a, e - and 16 - add a, 16 + ld hl, (ATTR_T) + push hl ; Saves ATTR_T since BUG ROM changes it -PRINT_TAB: - PROC - LOCAL LOOP, CONTINUE + 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 - 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 + pop hl + ld (STK_END), hl ; Balance STK_END to avoid STR$ bug -PRINT_AT: ; CHanges cursor to ROW, COL - ; COL in A register - ; ROW in stack + pop hl + ld (ATTR_T), hl ; Restores ATTR_T - pop hl ; Ret address - ex (sp), hl ; callee H = ROW - ld l, a - ex de, hl + ex de, hl ; String position now in HL - call __IN_SCREEN - ret nc ; Return if out of screen + push bc + xor a ; Avoid the str to be FREED from heap + call __PRINT_STR + pop bc + inc bc - jp __SAVE_S_POSN + jp RECLAIM2 ; Frees TMP Memory - 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 + RECLAIM2 EQU 19E8h - ENDP - + ENDP -#line 53 "print.bas" +#line 54 "print.bas" #line 1 "printf16.asm" #line 1 "printnum.asm" @@ -1907,8 +1927,8 @@ __PRINT_FIX_LOOP: ENDP -#line 54 "print.bas" -#line 1 "printu8.asm" +#line 55 "print.bas" + #line 1 "printi8.asm" #line 1 "div8.asm" @@ -2036,26 +2056,18 @@ __PRINTU_LOOP: ENDP -#line 2 "printu8.asm" - -#line 55 "print.bas" -#line 1 "print_eol_attr.asm" - ; Calls PRINT_EOL and then COPY_ATTR, so saves - ; 3 bytes - - +#line 57 "print.bas" +#line 1 "printu16.asm" -PRINT_EOL_ATTR: - call PRINT_EOL - jp COPY_ATTR -#line 56 "print.bas" - -#line 1 "printstr.asm" +#line 59 "print.bas" +#line 1 "printu8.asm" -#line 1 "free.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) @@ -2110,7 +2122,7 @@ PRINT_EOL_ATTR: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -2125,274 +2137,262 @@ PRINT_EOL_ATTR: + + ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory + ; MEM_ALLOC + ; Allocates a block of memory in the heap. ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done + ; 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_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) PROC - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - ld a, h - or l - ret z ; Return if NULL pointer + TEMP EQU TEMP0 + 1 - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + ld hl, 0 + ld (TEMP), hl +__MEM_START: 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? + 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 - 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 +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ; HL = Pointer to Free block + ld e, (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 - ld b, (hl) ; Loads block length into BC - dec hl - ld c, (hl) ; + inc hl ; DE = Block Length - 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 + 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 - ret nz + ld (TEMP), hl -__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 + 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 - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length - - ld b, h - ld c, l ; BC = Total Length + 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 - ld e, (hl) - inc hl - ld d, (hl) ; DE = block->next + 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 - pop hl ; Recovers Pointer to block - ld (hl), c - inc hl - ld (hl), b ; Length Saved - inc hl - ld (hl), e + ld (hl), c ; Store length on its 1st word inc hl - ld (hl), d ; Next saved + ld (hl), b + inc hl ; Return hl 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 +#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 ; Return if the pointer is NULL - - push hl + ret z - ld c, (hl) + ld a, (hl) inc hl - ld b, (hl) - inc hl ; BC = LEN(a$); HL = &a$ + ld h, (hl) ; LEN(str) in HL + ld l, a + ret -__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 +#line 3 "strcat.asm" -__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 +__ADDSTR: ; Implements c$ = a$ + b$ + ; hl = &a$, de = &b$ (pointers) -__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 +__STRCAT2: ; This routine creates a new string in dynamic space + ; making room for it. Then copies a$ + b$ into it. + ; HL = a$, DE = b$ -#line 58 "print.bas" -#line 1 "printf.asm" + PROC -#line 1 "stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- + 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$ - __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) + inc bc + inc bc ; +2 bytes to store length -__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 + ex de, hl + push hl + call __STRLEN + ; HL = len(b$) - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr + add hl, bc ; Total str length => 2 + len(a$) + len(b$) - pop af - pop de - pop bc + ld c, l + ld b, h ; BC = Total str length + 2 + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - - jp __FPSTACK_PUSH + ex de, hl ; HL = b$, DE = c$ + ex (sp), hl ; HL = a$, (SP) = b$ + exx + pop de ; D'E' = b$ + exx -__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" + pop bc ; LEN(a$) + ld a, d + or e + ret z ; If no memory: RETURN -__PRINTF: ; Prints a Fixed point Number stored in C ED LH - PROC +__STR_CONT: + push de ; Address of c$ - LOCAL RECLAIM2 - LOCAL STK_END - STK_END EQU 5C65h + ld a, h + or l + jr nz, __STR_CONT1 ; If len(a$) != 0 do copy - ld hl, (ATTR_T) - push hl ; Saves ATTR_T since BUG ROM changes it + ; 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 - ld hl, (STK_END) - push hl ; Stores STK_END +__STR_CONT1: ; Copies a$ (HL) into c$ (DE) + inc bc + inc bc ; BC = BC + 2 + ldir ; MEMCOPY: c$ = a$ + pop hl ; HL = c$ - 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 + exx + push de ; Recovers b$; A ex hl,hl' would be very handy + exx - pop hl - ld (STK_END), hl ; Balance STK_END to avoid STR$ bug + pop de ; DE = b$ - pop hl - ld (ATTR_T), hl ; Restores ATTR_T +__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) - ex de, hl ; String position now in HL + push hl ; Saves HL to return it later - push bc - xor a ; Avoid the str to be FREED from heap - call __PRINT_STR - pop bc - inc bc + 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 - jp RECLAIM2 ; Frees TMP Memory + ex de, hl ; DE = end of string (Begin of copy addr) + ld c, (hl) + inc hl + ld b, (hl) ; BC = len(b$) - RECLAIM2 EQU 19E8h + ld a, b + or c + jr z, __STRCATEND; Return if len(b$) == 0 - ENDP + push bc ; Save LEN(b$) + inc hl ; Skip 2nd byte of len(b$) + ldir ; Concatenate b$ -#line 59 "print.bas" + 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 -#line 1 "printu16.asm" + 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" diff --git a/tests/functional/save01.asm b/tests/functional/save01.asm index 833215af9..d4dab681d 100644 --- a/tests/functional/save01.asm +++ b/tests/functional/save01.asm @@ -47,142 +47,6 @@ __LABEL0: DEFB 6Eh DEFB 74h DEFB 73h -#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. - -#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_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 7 "save.asm" - -SAVE_CODE: - - PROC - - LOCAL MEMBOT - LOCAL SAVE_CONT - LOCAL ROM_SAVE - LOCAL __ERR_EMPTY - LOCAL SAVE_STOP - - 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 - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - ld a, b - or c - -__ERR_EMPTY: - ld a, ERROR_InvalidFileName - jr z, SAVE_STOP ; Return if str len == 0 - - ex de, hl ; Saves HL in DE - ld hl, 10 - or a - sbc hl, bc ; Test BC > 10? - ex de, hl - jr nc, SAVE_CONT ; Ok BC <= 10 - ld bc, 10 ; BC at most 10 chars - -SAVE_CONT: - ld de, MEMBOT + 1 - ldir ; Copy String block NAME - ld l, (ix + 13) - 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 36 "save01.bas" #line 1 "loadstr.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: @@ -252,7 +116,47 @@ SAVE_STOP: ; 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_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 @@ -417,9 +321,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/zxbasic/trunk/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/trunk/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -537,6 +441,102 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result 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 + 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 + + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, b + or c + +__ERR_EMPTY: + ld a, ERROR_InvalidFileName + jr z, SAVE_STOP ; Return if str len == 0 + + ex de, hl ; Saves HL in DE + ld hl, 10 + or a + sbc hl, bc ; Test BC > 10? + ex de, hl + jr nc, SAVE_CONT ; Ok BC <= 10 + ld bc, 10 ; BC at most 10 chars + +SAVE_CONT: + ld de, MEMBOT + 1 + ldir ; Copy String block NAME + ld l, (ix + 13) + 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: diff --git a/tests/functional/save02.asm b/tests/functional/save02.asm index f49e26d90..c36291968 100644 --- a/tests/functional/save02.asm +++ b/tests/functional/save02.asm @@ -44,142 +44,6 @@ __LABEL0: DEFB 73h DEFB 74h DEFB 31h -#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. - -#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_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 7 "save.asm" - -SAVE_CODE: - - PROC - - LOCAL MEMBOT - LOCAL SAVE_CONT - LOCAL ROM_SAVE - LOCAL __ERR_EMPTY - LOCAL SAVE_STOP - - 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 - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - ld a, b - or c - -__ERR_EMPTY: - ld a, ERROR_InvalidFileName - jr z, SAVE_STOP ; Return if str len == 0 - - ex de, hl ; Saves HL in DE - ld hl, 10 - or a - sbc hl, bc ; Test BC > 10? - ex de, hl - jr nc, SAVE_CONT ; Ok BC <= 10 - ld bc, 10 ; BC at most 10 chars - -SAVE_CONT: - ld de, MEMBOT + 1 - ldir ; Copy String block NAME - ld l, (ix + 13) - 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 "save02.bas" #line 1 "loadstr.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: @@ -249,7 +113,47 @@ SAVE_STOP: ; 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_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 @@ -414,9 +318,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/zxbasic/trunk/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/trunk/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -534,6 +438,102 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result 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 + 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 + + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, b + or c + +__ERR_EMPTY: + ld a, ERROR_InvalidFileName + jr z, SAVE_STOP ; Return if str len == 0 + + ex de, hl ; Saves HL in DE + ld hl, 10 + or a + sbc hl, bc ; Test BC > 10? + ex de, hl + jr nc, SAVE_CONT ; Ok BC <= 10 + ld bc, 10 ; BC at most 10 chars + +SAVE_CONT: + ld de, MEMBOT + 1 + ldir ; Copy String block NAME + ld l, (ix + 13) + 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: diff --git a/tests/functional/save03.asm b/tests/functional/save03.asm index d27987342..c2f49c239 100644 --- a/tests/functional/save03.asm +++ b/tests/functional/save03.asm @@ -43,142 +43,6 @@ __LABEL0: DEFB 65h DEFB 73h DEFB 74h -#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. - -#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_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 7 "save.asm" - -SAVE_CODE: - - PROC - - LOCAL MEMBOT - LOCAL SAVE_CONT - LOCAL ROM_SAVE - LOCAL __ERR_EMPTY - LOCAL SAVE_STOP - - 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 - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - ld a, b - or c - -__ERR_EMPTY: - ld a, ERROR_InvalidFileName - jr z, SAVE_STOP ; Return if str len == 0 - - ex de, hl ; Saves HL in DE - ld hl, 10 - or a - sbc hl, bc ; Test BC > 10? - ex de, hl - jr nc, SAVE_CONT ; Ok BC <= 10 - ld bc, 10 ; BC at most 10 chars - -SAVE_CONT: - ld de, MEMBOT + 1 - ldir ; Copy String block NAME - ld l, (ix + 13) - 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 32 "save03.bas" #line 1 "loadstr.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: @@ -248,7 +112,47 @@ SAVE_STOP: ; 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_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 @@ -413,9 +317,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/zxbasic/trunk/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/trunk/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -533,6 +437,102 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result 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 + 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 + + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, b + or c + +__ERR_EMPTY: + ld a, ERROR_InvalidFileName + jr z, SAVE_STOP ; Return if str len == 0 + + ex de, hl ; Saves HL in DE + ld hl, 10 + or a + sbc hl, bc ; Test BC > 10? + ex de, hl + jr nc, SAVE_CONT ; Ok BC <= 10 + ld bc, 10 ; BC at most 10 chars + +SAVE_CONT: + ld de, MEMBOT + 1 + ldir ; Copy String block NAME + ld l, (ix + 13) + 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: diff --git a/tests/functional/simple.asm b/tests/functional/simple.asm index e75bfad8a..e49b98724 100644 --- a/tests/functional/simple.asm +++ b/tests/functional/simple.asm @@ -47,7 +47,6 @@ __LABEL0: DEFB 52h DEFB 4Ch DEFB 44h -#line 1 "printstr.asm" #line 1 "print.asm" ; vim:ts=4:sw=4:et: ; PRINT command routine @@ -1153,7 +1152,9 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#line 35 "simple.bas" +#line 1 "printstr.asm" + #line 1 "free.asm" @@ -1525,8 +1526,7 @@ __PRINT_STR: ENDP -#line 35 "simple.bas" - +#line 36 "simple.bas" ZXBASIC_USER_DATA: ZXBASIC_MEM_HEAP: diff --git a/tests/functional/slice0.asm b/tests/functional/slice0.asm index a2150527d..2c0f20f71 100644 --- a/tests/functional/slice0.asm +++ b/tests/functional/slice0.asm @@ -579,9 +579,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/slice2.asm b/tests/functional/slice2.asm index 4aba50171..06d5d5ff6 100644 --- a/tests/functional/slice2.asm +++ b/tests/functional/slice2.asm @@ -124,6 +124,128 @@ __LABEL0: DEFB 72h 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 + +__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 113 "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 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + 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 114 "slice2.bas" #line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -440,42 +562,8 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ENDP -#line 113 "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 - ; 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 115 "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 @@ -544,47 +632,7 @@ __STRLEN: ; Direct FASTCALL entry ; 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_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" @@ -623,9 +671,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -700,181 +748,7 @@ __MEM_SUBTRACT: 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 114 "slice2.bas" -#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 115 "slice2.bas" - - -#line 1 "loadstr.asm" - +#line 2 "loadstr.asm" ; Loads a string (ptr) from HL ; and duplicates it on dynamic memory again @@ -917,7 +791,7 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result ret -#line 118 "slice2.bas" +#line 116 "slice2.bas" #line 1 "pstorestr2.asm" ; vim:ts=4:et:sw=4 ; @@ -974,6 +848,132 @@ __PSTORE_STR2: add hl, bc jp __STORE_STR2 +#line 117 "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 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 119 "slice2.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/spfill.asm b/tests/functional/spfill.asm index 113933661..454b9fa72 100644 --- a/tests/functional/spfill.asm +++ b/tests/functional/spfill.asm @@ -480,95 +480,6 @@ _SPFill__leave: __LABEL0: DEFW 0001h DEFB 61h -#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 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 470 "spfill.bas" #line 1 "circle.asm" ; Bresenham's like circle algorithm ; best known as Middle Point Circle drawing algorithm @@ -623,7 +534,39 @@ __STOP: #line 1 "in_screen.asm" +#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 2 "in_screen.asm" __IN_SCREEN: @@ -653,8 +596,56 @@ __OUT_OF_SCREEN_ERR: 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 + 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 10 "plot.asm" + PLOT: PROC @@ -943,6 +934,15 @@ __CIRCLE_PLOT: 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. diff --git a/tests/functional/storecstr.asm b/tests/functional/storecstr.asm index b338e042c..444e9c921 100644 --- a/tests/functional/storecstr.asm +++ b/tests/functional/storecstr.asm @@ -396,9 +396,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/storestr0.asm b/tests/functional/storestr0.asm index 0025352f3..c5187874c 100644 --- a/tests/functional/storestr0.asm +++ b/tests/functional/storestr0.asm @@ -389,9 +389,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/storestr1.asm b/tests/functional/storestr1.asm index 6e9a49758..3c2e64b16 100644 --- a/tests/functional/storestr1.asm +++ b/tests/functional/storestr1.asm @@ -69,28 +69,75 @@ __LABEL0: DEFB 6Ch DEFB 6Ch DEFB 6Fh -#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 "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 ; - -#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) + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). ; - ; e.g. => HL = _variableName (DIM _variableName$) - ; DE = Address into the HEAP + ; Please read the BSD license 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 function will resize (REALLOC) the space pointed by HL - ; before copying the content of b$ into a$ + ; This library must be initialized 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 "strcpy.asm" -#line 1 "realloc.asm" +#line 1 "heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -159,48 +206,208 @@ __LABEL0: ; 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 + ; --------------------------------------------------------------------- + ; __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 - ; Error code definitions (as in ZX spectrum manual) + 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 -; Set error code with: - ; ld a, ERROR_CODE - ; ld (ERR_NR), a + 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 - 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_InvalidFileName EQU 14 - ERROR_InvalidColour EQU 19 - ERROR_BreakIntoProgram EQU 20 - ERROR_TapeLoadingErr EQU 26 + 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 - ; Raises error using RST #8 -__ERROR: - ld (__ERROR_CODE), a - rst 8 -__ERROR_CODE: - nop - ret + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a - ; 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" + 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 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$) + ; 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) @@ -255,7 +462,7 @@ __STOP: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -269,7 +476,48 @@ __STOP: ; They will be added automatically if needed. -#line 1 "heapinit.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_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) @@ -324,7 +572,7 @@ __STOP: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -340,63 +588,6 @@ __STOP: - ; --------------------------------------------------------------------- - ; __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 @@ -433,9 +624,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -511,197 +702,7 @@ __MEM_SUBTRACT: #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" ; --------------------------------------------------------------------- @@ -925,8 +926,7 @@ __PSTORE_STR: add hl, bc jp __STORE_STR -#line 58 "storestr1.bas" - +#line 59 "storestr1.bas" ZXBASIC_USER_DATA: _a: diff --git a/tests/functional/storestr2.asm b/tests/functional/storestr2.asm index 5c4dc90f3..d26eed880 100644 --- a/tests/functional/storestr2.asm +++ b/tests/functional/storestr2.asm @@ -75,28 +75,7 @@ __LABEL0: DEFB 6Ch DEFB 6Ch DEFB 6Fh -#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$) - ; 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" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -164,49 +143,7 @@ __LABEL0: ; 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_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" +#line 1 "heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -261,7 +198,7 @@ __STOP: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -275,7 +212,188 @@ __STOP: ; They will be added automatically if needed. -#line 1 "heapinit.asm" + + + ; --------------------------------------------------------------------- + ; __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 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) @@ -330,7 +448,7 @@ __STOP: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -343,65 +461,48 @@ __STOP: ; 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 - ; --------------------------------------------------------------------- - ; __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 + ; Error code definitions (as in ZX spectrum manual) - ld b, h - ld c, l - inc bc - inc bc ; BC = starts of next block +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - 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 + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - 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 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret - ENDP + ; 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 70 "alloc.asm" ; --------------------------------------------------------------------- @@ -439,9 +540,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -516,8 +617,72 @@ __MEM_SUBTRACT: ENDP -#line 71 "realloc.asm" -#line 1 "free.asm" +#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 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$) + ; 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) @@ -587,127 +752,8 @@ __MEM_SUBTRACT: - ; --------------------------------------------------------------------- - ; 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" ; --------------------------------------------------------------------- @@ -931,52 +977,6 @@ __PSTORE_STR: add hl, bc jp __STORE_STR -#line 64 "storestr2.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 - 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 66 "storestr2.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/str0.asm b/tests/functional/str0.asm index fa7055c03..ac916f697 100644 --- a/tests/functional/str0.asm +++ b/tests/functional/str0.asm @@ -71,8 +71,7 @@ _test__leave: __LABEL0: DEFW 0001h DEFB 31h -#line 1 "strcat.asm" -#line 1 "alloc.asm" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -127,7 +126,7 @@ __LABEL0: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -140,47 +139,6 @@ __LABEL0: ; 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_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 @@ -307,414 +265,142 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" - +#line 69 "free.asm" ; --------------------------------------------------------------------- - ; MEM_ALLOC - ; Allocates a block of memory in the heap. - ; - ; Parameters - ; BC = Length of requested memory block + ; MEM_FREE + ; Frees a block of memory ; -; Returns: - ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) - ; if the block could not be allocated (out of memory) +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done ; --------------------------------------------------------------------- -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified PROC - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - TEMP EQU TEMP0 + 1 + ld a, h + or l + ret z ; Return if NULL pointer - ld hl, 0 - ld (TEMP), hl + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__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" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) + +__MEM_LOOP2: 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 + inc hl ; Next block ptr - ex de, hl ld e, (hl) inc hl - ld d, (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 - 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) + dec hl + + ld (hl), c 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), b ; (DE) <- BC - ld (hl), e + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) - ex de, hl + 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 (hl), d + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC dec hl - ld (hl), e ; Store new block length + ld c, (hl) ; - add hl, de ; New length + DE => free-block start - pop de ; Remove previous HL off the stack + 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 - ld (hl), c ; Store length on its 1st word +__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 - 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 + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length - ld a, (hl) - inc hl - ld h, (hl) ; LEN(str) in HL - ld l, a - ret + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next -#line 3 "strcat.asm" + 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 -__ADDSTR: ; Implements c$ = a$ + b$ - ; hl = &a$, de = &b$ (pointers) + ENDP +#line 59 "str0.bas" +#line 1 "print_eol_attr.asm" + ; Calls PRINT_EOL and then COPY_ATTR, so saves + ; 3 bytes -__STRCAT2: ; This routine creates a new string in dynamic space - ; making room for it. Then copies a$ + b$ into it. - ; HL = a$, DE = b$ +#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 __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 59 "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 "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 9 "str.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 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 - 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 60 "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) @@ -795,8 +481,48 @@ __CLS_SCR: #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_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) @@ -847,7 +573,18 @@ CALL_HL: ; 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 @@ -1760,18 +1497,79 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 5 "print_eol_attr.asm" +#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 + ; 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 -PRINT_EOL_ATTR: - call PRINT_EOL - jp COPY_ATTR -#line 61 "str0.bas" -#line 1 "printstr.asm" + 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 "free.asm" +#line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -1826,196 +1624,398 @@ PRINT_EOL_ATTR: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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. + ; 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/library-asm/alloc.asm" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/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 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 + ; 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 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 + 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 - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. + ret + RECLAIM2 EQU 19E8h + STK_END EQU 5C65h + ENDP - ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory - ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done - ; --------------------------------------------------------------------- +#line 62 "str0.bas" +#line 1 "strcat.asm" -MEM_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified - PROC +#line 1 "strlen.asm" + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +__STRLEN: ; Direct FASTCALL entry + ld a, h + or l + ret z - ld a, h - or l - ret z ; Return if NULL pointer + ld a, (hl) + inc hl + ld h, (hl) ; LEN(str) in HL + ld l, a + ret - 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 +#line 3 "strcat.asm" -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr +__ADDSTR: ; Implements c$ = a$ + b$ + ; hl = &a$, de = &b$ (pointers) - 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 +__STRCAT2: ; This routine creates a new string in dynamic space + ; making room for it. Then copies a$ + b$ into it. + ; HL = a$, DE = b$ - 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 + PROC - ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next + LOCAL __STR_CONT + LOCAL __STRCATEND -__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL - ex de, hl - push hl - dec hl + 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$ - ld (hl), c - inc hl - ld (hl), b ; (DE) <- BC + inc bc + inc bc ; +2 bytes to store length - 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 + ex de, hl + push hl + call __STRLEN + ; HL = len(b$) - call __MEM_JOIN_TEST - pop hl + add hl, bc ; Total str length => 2 + len(a$) + len(b$) -__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 + ld c, l + ld b, h ; BC = Total str length + 2 + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ -__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 ; HL = b$, DE = c$ + ex (sp), hl ; HL = a$, (SP) = b$ - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length + exx + pop de ; D'E' = b$ + exx - ld b, h - ld c, l ; BC = Total Length + pop bc ; LEN(a$) - ex de, hl - ld e, (hl) - inc hl - ld d, (hl) ; DE = block->next + ld a, d + or e + ret z ; If no memory: RETURN - 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 +__STR_CONT: + push de ; Address of c$ - ENDP + ld a, h + or l + jr nz, __STR_CONT1 ; If len(a$) != 0 do copy -#line 5 "printstr.asm" + ; 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 - ; PRINT command routine - ; Prints string pointed by HL +__STR_CONT1: ; Copies a$ (HL) into c$ (DE) + inc bc + inc bc ; BC = BC + 2 + ldir ; MEMCOPY: c$ = a$ + pop hl ; HL = c$ -PRINT_STR: -__PRINTSTR: ; __FASTCALL__ Entry to print_string - PROC - LOCAL __PRINT_STR_LOOP - LOCAL __PRINT_STR_END + exx + push de ; Recovers b$; A ex hl,hl' would be very handy + exx - ld d, a ; Saves A reg (Flag) for later + pop de ; DE = b$ - ld a, h - or l - ret z ; Return if the pointer is NULL +__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 + push hl ; Saves HL to return it later ld c, (hl) inc hl ld b, (hl) - inc hl ; BC = LEN(a$); HL = &a$ + 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$) -__PRINT_STR_LOOP: ld a, b or c - jr z, __PRINT_STR_END ; END if BC (counter = 0) + jr z, __STRCATEND; Return if len(b$) == 0 - ld a, (hl) - call __PRINTCHAR - inc hl - dec bc - jp __PRINT_STR_LOOP + push bc ; Save LEN(b$) + inc hl ; Skip 2nd byte of len(b$) + ldir ; Concatenate b$ -__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 + 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 -__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 + ld (hl), e ; Updates new LEN and return + inc hl + ld (hl), d + dec hl + ret - ENDP +__STRCATEND: + pop hl ; Removes Len(a$) + pop hl ; Restores original HL, so HL = a$ + ret -#line 62 "str0.bas" + ENDP +#line 63 "str0.bas" #line 1 "u32tofreg.asm" #line 1 "neg32.asm" __ABS32: diff --git a/tests/functional/str00.asm b/tests/functional/str00.asm index 1f178e2cd..857b38a81 100644 --- a/tests/functional/str00.asm +++ b/tests/functional/str00.asm @@ -393,9 +393,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/str01.asm b/tests/functional/str01.asm index fac003937..84a4aaf71 100644 --- a/tests/functional/str01.asm +++ b/tests/functional/str01.asm @@ -547,9 +547,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/str02.asm b/tests/functional/str02.asm index b53753f9b..80ad56307 100644 --- a/tests/functional/str02.asm +++ b/tests/functional/str02.asm @@ -324,9 +324,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -402,151 +402,6 @@ __MEM_SUBTRACT: #line 40 "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$ - - 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 41 "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. @@ -777,7 +632,7 @@ __STORE_STR2: ret -#line 42 "str02.bas" +#line 41 "str02.bas" #line 1 "str.asm" ; The STR$( ) BASIC function implementation @@ -913,6 +768,151 @@ __STR_END: 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$ + + 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 43 "str02.bas" #line 1 "u32tofreg.asm" #line 1 "neg32.asm" diff --git a/tests/functional/stradd.asm b/tests/functional/stradd.asm index 2e576424e..187f174c5 100644 --- a/tests/functional/stradd.asm +++ b/tests/functional/stradd.asm @@ -38,6 +38,13 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 +__LABEL0: + DEFW 0005h + DEFB 48h + DEFB 45h + DEFB 4Ch + DEFB 4Ch + DEFB 4Fh __LABEL1: DEFW 0006h DEFB 20h @@ -46,15 +53,21 @@ __LABEL1: DEFB 52h DEFB 4Ch DEFB 44h -__LABEL0: - DEFW 0005h - DEFB 48h - DEFB 45h - DEFB 4Ch - DEFB 4Ch - DEFB 4Fh -#line 1 "strcat.asm" -#line 1 "alloc.asm" +#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) @@ -109,7 +122,7 @@ __LABEL0: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -122,6 +135,7 @@ __LABEL0: ; 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: @@ -162,7 +176,76 @@ __ERROR_CODE: __STOP: ld (ERR_NR), a ret -#line 69 "alloc.asm" +#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 @@ -327,9 +410,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/home/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -404,235 +487,7 @@ __MEM_SUBTRACT: 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 42 "stradd.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 71 "realloc.asm" #line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -1039,7 +894,7 @@ __STORE_STR: pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret -#line 43 "stradd.bas" +#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. @@ -1080,6 +935,151 @@ __STORE_STR2: 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$ + + 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 44 "stradd.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/stringfunc.asm b/tests/functional/stringfunc.asm index ef17d4b9c..f6aff1d26 100644 --- a/tests/functional/stringfunc.asm +++ b/tests/functional/stringfunc.asm @@ -514,9 +514,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl diff --git a/tests/functional/stringparam.asm b/tests/functional/stringparam.asm index 636cee817..686a10899 100644 --- a/tests/functional/stringparam.asm +++ b/tests/functional/stringparam.asm @@ -65,8 +65,7 @@ __LABEL0: DEFB 6Ch DEFB 6Ch DEFB 6Fh -#line 1 "loadstr.asm" -#line 1 "alloc.asm" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -121,7 +120,7 @@ __LABEL0: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -134,47 +133,6 @@ __LABEL0: ; 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_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 @@ -301,7 +259,240 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" +#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 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) +; 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_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" + ; --------------------------------------------------------------------- @@ -459,8 +650,11 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result ret -#line 53 "stringparam.bas" -#line 1 "printstr.asm" +#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 @@ -1526,200 +1720,18 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes 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. - - - - ; --------------------------------------------------------------------- - ; 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 +#line 5 "print_eol_attr.asm" - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length - ld b, h - ld c, l ; BC = Total Length +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 55 "stringparam.bas" +#line 1 "printstr.asm" - 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 @@ -1772,18 +1784,6 @@ __PRINT_STR: ENDP -#line 54 "stringparam.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 56 "stringparam.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/strlocal0.asm b/tests/functional/strlocal0.asm index 22dddfc06..6b3f746d4 100644 --- a/tests/functional/strlocal0.asm +++ b/tests/functional/strlocal0.asm @@ -69,126 +69,446 @@ __LABEL0: DEFB 72h DEFB 6Ch DEFB 64h -#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 "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 -#line 1 "sposn.asm" - ; Printing positioning library. - PROC - LOCAL ECHO_E + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. -__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 - +; 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)| + ; +----------------+ -__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 + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). - 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 + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - ENDP + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. -#line 6 "print.asm" -#line 1 "cls.asm" - ; JUMPS directly to spectrum CLS - ; This routine does not clear lower screen +#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 - ;CLS EQU 0DAFh + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. - ; Our faster implementation +; 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). -CLS: - PROC - LOCAL COORDS - LOCAL __CLS_SCR - LOCAL ATTR_P - LOCAL SCREEN + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - 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 + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - ; 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 + ; --------------------------------------------------------------------- + ; __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 -#line 7 "print.asm" -#line 1 "in_screen.asm" + ; --------------------------------------------------------------------- + ; __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 -#line 1 "error.asm" - ; Simple error control routines -; vim:ts=4:et: + 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 - ERR_NR EQU 23610 ; Error code system variable + 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 - ; Error code definitions (as in ZX spectrum manual) + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block -; Set error code with: - ; ld a, ERROR_CODE - ; ld (ERR_NR), a + 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 - 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_InvalidFileName EQU 14 - ERROR_InvalidColour EQU 19 - ERROR_BreakIntoProgram EQU 20 - ERROR_TapeLoadingErr EQU 26 + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP - ; Raises error using RST #8 -__ERROR: - ld (__ERROR_CODE), a +#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 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 + +__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_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 @@ -1011,490 +1331,182 @@ __PRINT_INV2: 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" +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jp __PRINT_SET_STATE - ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory - ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done - ; --------------------------------------------------------------------- +__PRINT_OVR2: + exx + call OVER_TMP + jp __PRINT_RESTART -MEM_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified - PROC +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +__PRINT_BOLD2: + exx + call BOLD_TMP + jp __PRINT_RESTART - ld a, h - or l - ret z ; Return if NULL pointer +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer +__PRINT_ITA2: + exx + call ITALIC_TMP + jp __PRINT_RESTART - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start -__MEM_LOOP2: +__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 hl ; Next block ptr + inc de + djnz __BOLD_LOOP + pop hl + ld de, MEM0 + ret + - ld e, (hl) +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next + 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 - ld a, h ; HL == NULL? - or l - jp z, __MEM_LINK_PREV; if so, link with previous +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 - 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 +PRINT_TAB: + PROC + LOCAL LOOP, CONTINUE - ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next + 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 -__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL - ex de, hl - push hl - dec hl +PRINT_AT: ; CHanges cursor to ROW, COL + ; COL in A register + ; ROW in stack - ld (hl), c - inc hl - ld (hl), b ; (DE) <- BC + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl - 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 __IN_SCREEN + ret nc ; Return if out of screen - call __MEM_JOIN_TEST - pop hl + jp __SAVE_S_POSN -__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) ; + 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 - 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 + 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 -__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 + ENDP - 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 +#line 5 "print_eol_attr.asm" - ld b, h - ld c, l ; BC = Total Length - ex de, hl - ld e, (hl) - inc hl - ld d, (hl) ; DE = block->next +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 58 "strlocal0.bas" +#line 1 "printstr.asm" + - 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 @@ -1547,7 +1559,7 @@ __PRINT_STR: ENDP -#line 57 "strlocal0.bas" +#line 59 "strlocal0.bas" #line 1 "pstorestr.asm" ; vim:ts=4:et:sw=4 ; @@ -2048,18 +2060,6 @@ __PSTORE_STR: add hl, bc jp __STORE_STR -#line 58 "strlocal0.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 60 "strlocal0.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/strparam0.asm b/tests/functional/strparam0.asm index 2a3ff2c8f..e5f1b4010 100644 --- a/tests/functional/strparam0.asm +++ b/tests/functional/strparam0.asm @@ -95,8 +95,7 @@ __LABEL0: DEFB 72h DEFB 6Ch DEFB 64h -#line 1 "loadstr.asm" -#line 1 "alloc.asm" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -151,7 +150,7 @@ __LABEL0: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -164,47 +163,6 @@ __LABEL0: ; 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_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 @@ -331,7 +289,240 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" +#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 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) +; 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_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" + ; --------------------------------------------------------------------- @@ -489,8 +680,11 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result ret -#line 83 "strparam0.bas" -#line 1 "printstr.asm" +#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 @@ -1556,200 +1750,18 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes 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. - - - - ; --------------------------------------------------------------------- - ; 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 +#line 5 "print_eol_attr.asm" - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length - ld b, h - ld c, l ; BC = Total Length +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 85 "strparam0.bas" +#line 1 "printstr.asm" - 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 @@ -1802,18 +1814,6 @@ __PRINT_STR: ENDP -#line 84 "strparam0.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 86 "strparam0.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/strparam1.asm b/tests/functional/strparam1.asm index 6f89cd862..db9053a02 100644 --- a/tests/functional/strparam1.asm +++ b/tests/functional/strparam1.asm @@ -79,7 +79,6 @@ __LABEL0: DEFB 6Ch DEFB 6Fh DEFB 20h -#line 1 "strcat.asm" #line 1 "alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -353,9 +352,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -430,288 +429,220 @@ __MEM_SUBTRACT: 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 +#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) +; 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 -__STRLEN: ; Direct FASTCALL entry - ld a, h - or l - ret z + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. - ld a, (hl) - inc hl - ld h, (hl) ; LEN(str) in HL - ld l, a - ret +; 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)| + ; +----------------+ -#line 3 "strcat.asm" + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). -__ADDSTR: ; Implements c$ = a$ + b$ - ; hl = &a$, de = &b$ (pointers) + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. -__STRCAT2: ; This routine creates a new string in dynamic space - ; making room for it. Then copies a$ + b$ into it. - ; HL = a$, DE = b$ + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - 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$ + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- - inc bc - inc bc ; +2 bytes to store length +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC - ex de, hl - push hl - call __STRLEN - ; HL = len(b$) + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - add hl, bc ; Total str length => 2 + len(a$) + len(b$) + ld a, h + or l + ret z ; Return if NULL pointer - ld c, l - ld b, h ; BC = Total str length + 2 - call __MEM_ALLOC - pop de ; HL = c$, DE = b$ + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer - ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start - exx - pop de ; D'E' = b$ - exx +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr - pop bc ; LEN(a$) + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next - ld a, d - or e - ret z ; If no memory: RETURN + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous -__STR_CONT: - push de ; Address of c$ + 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 - ld a, h - or l - jr nz, __STR_CONT1 ; If len(a$) != 0 do copy + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next - ; 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 +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl -__STR_CONT1: ; Copies a$ (HL) into c$ (DE) - inc bc - inc bc ; BC = BC + 2 - ldir ; MEMCOPY: c$ = a$ - pop hl ; HL = c$ + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC - exx - push de ; Recovers b$; A ex hl,hl' would be very handy - exx + 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 - pop de ; DE = b$ + call __MEM_JOIN_TEST + pop hl -__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) +__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 - push hl ; Saves HL to return it later +__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 - 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 = &(block->next) + add hl, bc ; HL = Total Length - ex de, hl ; DE = end of string (Begin of copy addr) - ld c, (hl) - inc hl - ld b, (hl) ; BC = len(b$) + ld b, h + ld c, l ; BC = Total Length - ld a, b - or c - jr z, __STRCATEND; Return if len(b$) == 0 + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next - push bc ; Save LEN(b$) - inc hl ; Skip 2nd byte of len(b$) - ldir ; Concatenate b$ + 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 - 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 68 "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 - ; 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 9 "str.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 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 - 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 + ENDP - RECLAIM2 EQU 19E8h - STK_END EQU 5C65h +#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 + ; - ENDP +#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 69 "strparam1.bas" -#line 1 "free.asm" +#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) @@ -781,287 +712,75 @@ __STR_END: + + + + ; --------------------------------------------------------------------- - ; MEM_FREE - ; Frees a block of memory + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. ; -; Parameters: - ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing - ; is done + ; 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. + ; ; --------------------------------------------------------------------- - -MEM_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified +__REALLOC: ; Reallocates block pointed by HL, with new length BC PROC - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN + LOCAL __REALLOC_END ld a, h or l - ret z ; Return if NULL pointer + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr + dec hl ; HL = Block start - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next + 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 ; HL == NULL? + ld a, h or l - jp z, __MEM_LINK_PREV; if so, link with previous + ret z ; Return if HL == NULL (No memory) + + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL - 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 + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) - ;------ 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 71 "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$) - ; 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) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) exx push de @@ -1218,6 +937,287 @@ __PSTORE_STR: 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 + ; 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 9 "str.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 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 + 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$ + + 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 72 "strparam1.bas" #line 1 "u32tofreg.asm" #line 1 "neg32.asm" diff --git a/tests/functional/strparam2.asm b/tests/functional/strparam2.asm index 6bd8759cf..22d51f398 100644 --- a/tests/functional/strparam2.asm +++ b/tests/functional/strparam2.asm @@ -82,7 +82,10 @@ __LABEL0: DEFB 53h DEFB 49h DEFB 43h -#line 1 "printstr.asm" +#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 @@ -1188,7 +1191,15 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes ENDP -#line 2 "printstr.asm" +#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" @@ -1560,7 +1571,7 @@ __PRINT_STR: ENDP -#line 70 "strparam2.bas" +#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 @@ -2046,17 +2057,6 @@ __STORE_STR: pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret -#line 71 "strparam2.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 72 "strparam2.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/strparam3.asm b/tests/functional/strparam3.asm index 0b0f5ab89..a7a241eb1 100644 --- a/tests/functional/strparam3.asm +++ b/tests/functional/strparam3.asm @@ -89,8 +89,7 @@ __LABEL0: DEFB 53h DEFB 49h DEFB 43h -#line 1 "loadstr.asm" -#line 1 "alloc.asm" +#line 1 "free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -145,7 +144,7 @@ __LABEL0: ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -158,47 +157,6 @@ __LABEL0: ; 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_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 @@ -325,7 +283,240 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" +#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 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) +; 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_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" + ; --------------------------------------------------------------------- @@ -483,8 +674,11 @@ __LOADSTR: ; __FASTCALL__ entry ldir ; Copies string (length number included) pop hl ; Recovers destiny in hl as result ret -#line 77 "strparam3.bas" -#line 1 "printstr.asm" +#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 @@ -1550,200 +1744,18 @@ __PRINT_TABLE: ; Jump table for 0 .. 22 codes 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. - - - - ; --------------------------------------------------------------------- - ; 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 +#line 5 "print_eol_attr.asm" - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length - ld b, h - ld c, l ; BC = Total Length +PRINT_EOL_ATTR: + call PRINT_EOL + jp COPY_ATTR +#line 79 "strparam3.bas" +#line 1 "printstr.asm" - 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 @@ -1796,18 +1808,6 @@ __PRINT_STR: ENDP -#line 78 "strparam3.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 80 "strparam3.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/strsigil.asm b/tests/functional/strsigil.asm index eb3e58d09..253001735 100644 --- a/tests/functional/strsigil.asm +++ b/tests/functional/strsigil.asm @@ -46,151 +46,9 @@ __END_PROGRAM: ret __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 - - 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, __NEG32 ; Negates DEHL - - ret - - ENDP - - -__FTOU8: ; Converts float in C ED LH to Unsigned byte in A - call __FTOU32REG - ld a, l - ret - -#line 35 "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 - ; 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" +#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) @@ -245,7 +103,7 @@ __STRLEN: ; Direct FASTCALL entry ; When 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 + ; if we can defragment the heap. If the block to be breed is just next to the ; previous, or to the next (or both) they will be converted into a single ; block (so defragmented). @@ -258,47 +116,6 @@ __STRLEN: ; Direct FASTCALL entry ; 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_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 @@ -425,123 +242,308 @@ __MEM_INIT2: ENDP -#line 70 "alloc.asm" - +#line 69 "free.asm" ; --------------------------------------------------------------------- - ; MEM_ALLOC - ; Allocates a block of memory in the heap. - ; - ; Parameters - ; BC = Length of requested memory block + ; MEM_FREE + ; Frees a block of memory ; -; Returns: - ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) - ; if the block could not be allocated (out of memory) +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done ; --------------------------------------------------------------------- -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified PROC - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN - TEMP EQU TEMP0 + 1 + ld a, h + or l + ret z ; Return if NULL pointer - ld hl, 0 - ld (TEMP), hl + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer -__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/spyder/zxbasic/library-asm/alloc.asm" - ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" - ; HL = Pointer to Free block - ld e, (hl) + +__MEM_LOOP2: 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 + inc hl ; Next block ptr - 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) + 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 - 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) + dec hl + + ld (hl), c 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), b ; (DE) <- BC - ld (hl), e + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) - ex de, hl + 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 (hl), d + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC dec hl - ld (hl), e ; Store new block length + ld c, (hl) ; - add hl, de ; New length + DE => free-block start - pop de ; Remove previous HL off the stack + 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 - ld (hl), c ; Store length on its 1st word +__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 (hl), b - inc hl ; Return 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 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 + + 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, __NEG32 ; Negates DEHL + + ret + + 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 + ; 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 19 "strslice.asm" -#line 1 "free.asm" +#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) @@ -596,7 +598,7 @@ __MEM_SUBTRACT: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -609,129 +611,164 @@ __MEM_SUBTRACT: ; 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 - ; --------------------------------------------------------------------- - ; 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 + ; Error code definitions (as in ZX spectrum manual) - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - ld a, h - or l - ret z ; Return if NULL pointer - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next + ; 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" - 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_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_LINK_PREV: ; Link (DE) with BC, and BC->next with HL - ex de, hl - push hl - dec hl +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC - ld (hl), c - inc hl - ld (hl), b ; (DE) <- BC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 - 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 + TEMP EQU TEMP0 + 1 - call __MEM_JOIN_TEST - pop hl + ld hl, 0 + ld (TEMP), hl -__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them - ; hl = Ptr to current block + 2 - ld d, (hl) - dec 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" + ret z ; NULL +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" + ; HL = Pointer to Free block ld e, (hl) - dec hl - ld b, (hl) ; Loads block length into BC - dec hl - ld c, (hl) ; + inc hl + ld d, (hl) + inc hl ; DE = Block Length - 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 + 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 - ret nz + ld (TEMP), hl -__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 + ld e, (hl) 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) + 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 d, (hl) ; DE = block->next + 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 - 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 + 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 20 "strslice.asm" + +#line 19 "strslice.asm" + __STRSLICE: ; Callee entry pop hl ; Return ADDRESS @@ -819,43 +856,6 @@ __FREE_ON_EXIT: ENDP -#line 36 "strsigil.bas" -#line 1 "asc.asm" - ; Returns the ascii code for the given str - - -__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 37 "strsigil.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/substrlval.asm b/tests/functional/substrlval.asm index 7d9d576ba..d5d817aeb 100644 --- a/tests/functional/substrlval.asm +++ b/tests/functional/substrlval.asm @@ -57,21 +57,17 @@ __LABEL0: __LABEL1: DEFW 0001h DEFB 7Ah -#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 "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 "strcpy.asm" -#line 1 "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) @@ -139,49 +135,7 @@ __LABEL1: ; 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_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" +#line 1 "heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -235,22 +189,353 @@ __STOP: ; +----------------+ - ; When 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). + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the 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 - ; MEMORY MANAGER - ; - ; This library must be initialized calling __MEM_INIT with - ; HL = BLOCK Start & DE = Length. +__FREE_STR0: + ex de, hl - ; An init directive is useful for initialization routines. - ; They will be added automatically if needed. +__FREE_STR: + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + ENDP -#line 1 "heapinit.asm" +#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) @@ -305,7 +590,7 @@ __STOP: ; When a block is FREED, the previous and next pointers are examined to see - ; if we can defragment the heap. If the block to be breed is just next to the + ; 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). @@ -318,65 +603,48 @@ __STOP: ; 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 - ; --------------------------------------------------------------------- - ; __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 + ; Error code definitions (as in ZX spectrum manual) - ld b, h - ld c, l - inc bc - inc bc ; BC = starts of next block +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a - 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 + 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_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 - 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 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret - ENDP + ; 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 70 "alloc.asm" ; --------------------------------------------------------------------- @@ -414,9 +682,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -491,8 +759,65 @@ __MEM_SUBTRACT: ENDP -#line 71 "realloc.asm" -#line 1 "free.asm" +#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 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$) + ; 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) @@ -548,141 +873,22 @@ __MEM_SUBTRACT: ; When a block is FREED, the previous and next pointers are examined to see ; if we can defragment the heap. If the block to be breed is just next to the - ; previous, or to the 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 + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). -__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 + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. - 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" ; --------------------------------------------------------------------- @@ -898,212 +1104,6 @@ __STORE_STR: pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret -#line 46 "substrlval.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 - 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 47 "substrlval.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 - - - -__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 48 "substrlval.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/test.py b/tests/functional/test.py index 398583e73..e7bc8b987 100755 --- a/tests/functional/test.py +++ b/tests/functional/test.py @@ -8,7 +8,6 @@ import subprocess import difflib - BUFFSIZE = 1024 CLOSE_STDERR = False reOPT = re.compile(r'^opt([0-9]+)_') # To detect -On tests @@ -22,6 +21,8 @@ COUNTER = 0 FAILED = 0 UPDATE = False # True and test will be updated +FOUT = sys.stdout # Output file +ZXBASIC_ROOT = os.path.abspath('../..') # -------------------------------------------------- @@ -33,8 +34,8 @@ def get_file_lines(filename, ignore_regexp=None, replace_regexp=None, """ Opens source file and load its line, discarding those not important for comparison. """ - with open(filename, 'rb') as f: - lines = f.readlines() + with open(filename, 'rt') as f: + lines = [x for x in f] if ignore_regexp is not None: r = re.compile(ignore_regexp) @@ -48,13 +49,13 @@ def get_file_lines(filename, ignore_regexp=None, replace_regexp=None, def is_same_file(fname1, fname2, ignore_regexp=None, - replace_regexp=None, replace_what='.', replace_with='.', diff=None): + replace_regexp=None, replace_what='.', replace_with='.', diff=None, is_binary=False): """ Test if two files are the same. - If ignore_regep is passed, it must be a Regular Expression + If ignore_regexp is passed, it must be a Regular Expression which will ignore matched lines on both files. - If replace_regexp is passed, al lines matching RE (string) will perform + If replace_regexp is passed, all lines matching RE (string) will perform a string substitution of A into B. This if done *AFTER* ignoreLinesRE. """ if fname1 == fname2: @@ -66,6 +67,9 @@ def is_same_file(fname1, fname2, ignore_regexp=None, if not os.path.exists(fname1) or not os.path.exists(fname2): return False + if is_binary: + return open(fname1, 'rb').read() == open(fname2, 'rb').read() + r1 = get_file_lines(fname1, ignore_regexp, replace_regexp, replace_what, replace_with) r2 = get_file_lines(fname2, ignore_regexp, replace_regexp, replace_what, replace_with) result = (r1 == r2) @@ -88,7 +92,7 @@ def systemExec(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT): result = subprocess.Popen(command, bufsize=-1, shell=True, stdout=stdout, stderr=stderr) exit_code = result.wait() - print result.stdout.read(), + FOUT.write(result.stdout.read().decode('utf-8')) return exit_code @@ -99,7 +103,7 @@ def getExtension(fname): split = os.path.basename(fname).split(os.extsep) if len(split) > 1: return split[-1] - + return None @@ -113,26 +117,6 @@ def getName(fname): return basename.split(os.extsep)[0] -def testASM(fname): - tfname = 'test' + fname + os.extsep + 'bin' - prep = ' -e /dev/null' if CLOSE_STDERR else '' - - if systemExec('./zxbasm.py ' + fname + ' -o ' + tfname + prep): - try: - os.unlink(tfname) - except OSError: - pass - - okfile = getName(fname) + os.extsep + 'bin' - result = is_same_file(okfile, tfname) - try: - os.unlink(tfname) - except OSError: - pass - - return result - - def _get_testbas_cmdline(fname): """ Generates a command line string to be executed to get the .asm test file from a .bas one. @@ -167,6 +151,26 @@ def _get_testbas_cmdline(fname): return cmdline, tfname, ext +def testASM(fname): + tfname = 'test' + fname + os.extsep + 'bin' + prep = ' -e /dev/null' if CLOSE_STDERR else '' + + if systemExec('./zxbasm.py ' + fname + ' -o ' + tfname + prep): + try: + os.unlink(tfname) + except OSError: + pass + + okfile = getName(fname) + os.extsep + 'bin' + result = is_same_file(okfile, tfname, is_binary=True) + try: + os.unlink(tfname) + except OSError: + pass + + return result + + def testBAS(fname, filter_=None): """ filter_ will be ignored for binary (tzx, tap, etc) files """ @@ -181,7 +185,7 @@ def testBAS(fname, filter_=None): return okfile = getName(fname) + os.extsep + ext - result = is_same_file(okfile, tfname, filter_) + result = is_same_file(okfile, tfname, filter_, is_binary=reBIN.match(fname) is not None) try: os.unlink(tfname) @@ -191,14 +195,14 @@ def testBAS(fname, filter_=None): return result -def testPREPRO(fname, pattern_ = None): +def testPREPRO(fname, pattern_=None): tfname = 'test' + fname + os.extsep + 'out' prep = ' 2> /dev/null' if CLOSE_STDERR else '' OPTIONS = '' match = reOPT.match(getName(fname)) if match: OPTIONS = ' -O' + match.groups()[0] + ' ' - + if systemExec('./zxbpp.py ' + OPTIONS + fname + ' >' + tfname + prep): try: os.unlink(tfname) @@ -236,17 +240,17 @@ def testFiles(file_list): result = None COUNTER += 1 - print ("%4i " % COUNTER) + fname + ':', + FOUT.write(("%4i " % COUNTER) + fname + ':') if result: - sys.stdout.write('ok \r') - sys.stdout.flush() + FOUT.write('ok \r') + FOUT.flush() elif result is None: - print '?\r', + FOUT.write('?\r') else: FAILED += 1 EXIT_CODE = 1 - print 'FAIL' + FOUT.write('FAIL\n') def upgradeTest(fileList, f3diff): @@ -254,6 +258,7 @@ def upgradeTest(fileList, f3diff): If the diff between file1 and file2 are the same as file3, then the .asm file is patched. """ + def normalizeDiff(diff): diff = [x.strip(' \t') for x in diff] @@ -279,7 +284,7 @@ def normalizeDiff(diff): O2 = int(g[2]) diff[i] = "@@ -%(a)s%(b)s +%(c)s%(d)s\n" % \ - {'a': int(g[0]) - O1, 'b': g[1], 'c': int(g[2]) - O2, 'd': g[3]} + {'a': int(g[0]) - O1, 'b': g[1], 'c': int(g[2]) - O2, 'd': g[3]} return diff @@ -319,11 +324,11 @@ def normalizeDiff(diff): os.unlink(fname1) os.rename(tfname, fname1) - print "\rTest: %s (%s) updated" % (fname, fname1) + print("\rTest: %s (%s) updated" % (fname, fname1)) def help_(): - print """{0}\n + print("""{0}\n Usage: {0} [params] @@ -338,7 +343,7 @@ def help_(): {0} -vd *.bas # Checks for any *.bas test and displays diffs {0} -u b.diff a*.bas # Updates all a*.bas tests if the b.diff matches {0} -U b*.bas # Updates b test with the output of the current compiler - """.format(sys.argv[0]) + """.format(sys.argv[0])) sys.exit(2) @@ -346,10 +351,8 @@ def check_arg(i): if len(sys.argv) <= i: help_() - -if __name__ == '__main__': - ZXBASIC_ROOT = os.path.abspath('../..') +if __name__ == '__main__': i = 1 check_arg(i) @@ -373,6 +376,6 @@ def check_arg(i): check_arg(i) testFiles(sys.argv[i:]) - print "Total: %i, Failed: %i (%3.2f%%)" % (COUNTER, FAILED, 100.0 * FAILED / float(COUNTER)) + print("Total: %i, Failed: %i (%3.2f%%)" % (COUNTER, FAILED, 100.0 * FAILED / float(COUNTER))) sys.exit(EXIT_CODE) diff --git a/tests/functional/test_.py b/tests/functional/test_.py index ccb8258c8..b2fdf1489 100755 --- a/tests/functional/test_.py +++ b/tests/functional/test_.py @@ -1,7 +1,11 @@ #!/usr/bin/env python # vim:ts=4:et:ai -''' +import doctest +import test + + +""" >>> test.testBAS('doloop1.bas') doloop1.bas:2: warning: Infinite empty loop True @@ -43,13 +47,23 @@ >>> test.testBAS('typecast2.bas') typecast2.bas:10: Cannot convert string to a value. Use VAL() function True -''' +""" -import test -if __name__ == '__main__': +class OutputProxy(object): + """A simple interface to replace sys.stdout so + doctest can capture it. + """ + def write(self, str_): + print(str_) + + +def main(): import doctest + test.FOUT = OutputProxy() doctest.testmod() +if __name__ == '__main__': + main() diff --git a/tests/functional/valcrash2.asm b/tests/functional/valcrash2.asm index b017c27e1..7df6a11dd 100644 --- a/tests/functional/valcrash2.asm +++ b/tests/functional/valcrash2.asm @@ -46,7 +46,12 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#line 1 "strcat.asm" +#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 @@ -320,9 +325,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 111 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 111 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ret z ; NULL -#line 113 "/Users/boriel/Documents/src/spyder/zxbasic/library-asm/alloc.asm" +#line 113 "/Users/boriel/Documents/src/zxbasic/library-asm/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -397,7 +402,94 @@ __MEM_SUBTRACT: ENDP -#line 2 "strcat.asm" +#line 7 "inkey.asm" + +INKEY: + PROC + LOCAL __EMPTY_INKEY + LOCAL KEY_SCAN + LOCAL KEY_TEST + LOCAL KEY_CODE + + ld bc, 3 ; 1 char length string + call __MEM_ALLOC + + ld a, h + or l + ret z ; Return if NULL (No memory) + + push hl ; Saves memory pointer + + call KEY_SCAN + jp nz, __EMPTY_INKEY + + call KEY_TEST + jp nc, __EMPTY_INKEY + + dec d ; D is expected to be FLAGS so set bit 3 $FF + ; 'L' Mode so no keywords. + ld e, a ; main key to A + ; C is MODE 0 'KLC' from above still. + call KEY_CODE ; routine K-DECODE + pop hl + + ld (hl), 1 + inc hl + ld (hl), 0 + inc hl + ld (hl), a + dec hl + dec hl ; HL Points to string result + ret + +__EMPTY_INKEY: + pop hl + xor a + ld (hl), a + inc hl + ld (hl), a + dec hl + ret + + KEY_SCAN EQU 028Eh + KEY_TEST EQU 031Eh + KEY_CODE EQU 0333h + + ENDP + +#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 + 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 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 @@ -540,69 +632,7 @@ __STRCATEND: ENDP -#line 35 "valcrash2.bas" -#line 1 "inkey.asm" - ; INKEY Function - ; Returns a string allocated in dynamic memory - ; containing the string. - ; An empty string otherwise. - - - -INKEY: - PROC - LOCAL __EMPTY_INKEY - LOCAL KEY_SCAN - LOCAL KEY_TEST - LOCAL KEY_CODE - - ld bc, 3 ; 1 char length string - call __MEM_ALLOC - - ld a, h - or l - ret z ; Return if NULL (No memory) - - push hl ; Saves memory pointer - - call KEY_SCAN - jp nz, __EMPTY_INKEY - - call KEY_TEST - jp nc, __EMPTY_INKEY - - dec d ; D is expected to be FLAGS so set bit 3 $FF - ; 'L' Mode so no keywords. - ld e, a ; main key to A - ; C is MODE 0 'KLC' from above still. - call KEY_CODE ; routine K-DECODE - pop hl - - ld (hl), 1 - inc hl - ld (hl), 0 - inc hl - ld (hl), a - dec hl - dec hl ; HL Points to string result - ret - -__EMPTY_INKEY: - pop hl - xor a - ld (hl), a - inc hl - ld (hl), a - dec hl - ret - - KEY_SCAN EQU 028Eh - KEY_TEST EQU 031Eh - KEY_CODE EQU 0333h - - ENDP - -#line 36 "valcrash2.bas" +#line 37 "valcrash2.bas" #line 1 "val.asm" #line 1 "free.asm" ; vim: ts=4:et:sw=4: @@ -959,36 +989,6 @@ __RET_ZERO: ; Returns 0 Floating point on error ENDP -#line 37 "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 - 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 38 "valcrash2.bas" ZXBASIC_USER_DATA: diff --git a/tests/functional/while.asm b/tests/functional/while.asm index 5b890ef87..063426459 100644 --- a/tests/functional/while.asm +++ b/tests/functional/while.asm @@ -54,36 +54,6 @@ __END_PROGRAM: ret __CALL_BACK__: DEFW 0 -#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 46 "while.bas" #line 1 "ltf.asm" #line 1 "u32tofreg.asm" #line 1 "neg32.asm" @@ -355,6 +325,36 @@ __LTF: ; A < B call __FPSTACK_POP jp __FTOU8 ; Convert to 8 bits +#line 46 "while.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 "while.bas" ZXBASIC_USER_DATA: diff --git a/tests/symbols/__init__.py b/tests/symbols/__init__.py index b6ac7f1da..475b5c013 100644 --- a/tests/symbols/__init__.py +++ b/tests/symbols/__init__.py @@ -3,8 +3,7 @@ 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)) +path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir)) sys.path.insert(0, path) diff --git a/tests/symbols/test_symbolARGLIST.py b/tests/symbols/test_symbolARGLIST.py index 24fc8b25f..cfcab03a6 100644 --- a/tests/symbols/test_symbolARGLIST.py +++ b/tests/symbols/test_symbolARGLIST.py @@ -3,15 +3,13 @@ import unittest from unittest import TestCase - -# Initialize import syspath -import __init__ - import symbols + class TestSymbolARGLIST(TestCase): def setUp(self): self.VALUE = 5 + self.value = symbols.NUMBER(self.VALUE, 1) self.a = symbols.ARGLIST(symbols.ARGUMENT(symbols.NUMBER(self.VALUE, 1), 1)) def test__len__(self): @@ -20,11 +18,11 @@ def test__len__(self): self.assertEqual(len(b), 0) def test_args(self): - self.assertEqual(self.a[0], self.VALUE) + self.assertEqual(self.a[0], self.value) def test_args_setter(self): self.a[0] = symbols.ARGUMENT(symbols.NUMBER(self.VALUE + 1, 1), 1) - self.assertEqual(self.a[0], self.VALUE + 1) + self.assertEqual(self.a[0], self.value + 1) def test_args_setter_fail(self): self.assertRaises(AssertionError, symbols.ARGLIST.__setitem__, self.a, 0, 'blah') @@ -38,13 +36,13 @@ def test_make_node_single(self): b = symbols.ARGLIST.make_node(symbols.ARGUMENT(symbols.NUMBER(self.VALUE, 1), 1)) self.assertIsInstance(b, symbols.ARGLIST) self.assertEqual(len(b), 1) - self.assertEqual(b[0], self.VALUE) + self.assertEqual(b[0], self.value) def test_make_node_single2(self): b = symbols.ARGLIST.make_node(None, symbols.ARGUMENT(symbols.NUMBER(self.VALUE, 1), 1)) self.assertIsInstance(b, symbols.ARGLIST) self.assertEqual(len(b), 1) - self.assertEqual(b[0], self.VALUE) + self.assertEqual(b[0], self.value) def test_make_node_fails(self): self.assertRaises(AssertionError, symbols.ARGLIST.make_node, 'blah') diff --git a/tests/symbols/test_symbolARRAYACCESS.py b/tests/symbols/test_symbolARRAYACCESS.py index 202f824fe..3100f419f 100644 --- a/tests/symbols/test_symbolARRAYACCESS.py +++ b/tests/symbols/test_symbolARRAYACCESS.py @@ -1,9 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import unittest from unittest import TestCase -from StringIO import StringIO +from six import StringIO # Initialize import syspath import __init__ diff --git a/tests/symbols/test_symbolBINARY.py b/tests/symbols/test_symbolBINARY.py index 556860015..ee099da27 100644 --- a/tests/symbols/test_symbolBINARY.py +++ b/tests/symbols/test_symbolBINARY.py @@ -1,9 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import unittest from unittest import TestCase -from StringIO import StringIO +from six import StringIO # Initialize import syspath import __init__ diff --git a/tests/symbols/test_symbolBOUND.py b/tests/symbols/test_symbolBOUND.py index d45b6e651..c3fe072ea 100644 --- a/tests/symbols/test_symbolBOUND.py +++ b/tests/symbols/test_symbolBOUND.py @@ -3,7 +3,7 @@ import unittest from unittest import TestCase -from StringIO import StringIO +from six import StringIO # Initialize import syspath import __init__ diff --git a/tests/symbols/test_symbolBOUNDLIST.py b/tests/symbols/test_symbolBOUNDLIST.py index f6d366c11..2d115cf2b 100644 --- a/tests/symbols/test_symbolBOUNDLIST.py +++ b/tests/symbols/test_symbolBOUNDLIST.py @@ -3,7 +3,6 @@ import unittest from unittest import TestCase -from StringIO import StringIO # Initialize import syspath import __init__ diff --git a/tests/symbols/test_symbolFUNCDECL.py b/tests/symbols/test_symbolFUNCDECL.py index 0e70841b3..3e3518481 100644 --- a/tests/symbols/test_symbolFUNCDECL.py +++ b/tests/symbols/test_symbolFUNCDECL.py @@ -3,7 +3,6 @@ import unittest from unittest import TestCase -from StringIO import StringIO import api.global_ as gl import api.symboltable diff --git a/tests/symbols/test_symbolNUMBER.py b/tests/symbols/test_symbolNUMBER.py index 7a168538f..1219d1cab 100644 --- a/tests/symbols/test_symbolNUMBER.py +++ b/tests/symbols/test_symbolNUMBER.py @@ -8,7 +8,6 @@ import __init__ from api.constants import TYPE -from api.config import OPTIONS from symbols import NUMBER from symbols import BASICTYPE @@ -30,7 +29,6 @@ def test__init__(self): n = NUMBER(-256, lineno=1) self.assertEqual(n.type_, BASICTYPE(TYPE.integer)) - def test__cmp__(self): n = NUMBER(0, lineno=1) m = NUMBER(1, lineno=2) @@ -46,7 +44,6 @@ def test__cmp__(self): self.assertGreater(m, n) self.assertLess(n, m) - def test__t(self): n = NUMBER(3.14, 1) self.assertEqual(n.t, '3.14') diff --git a/tests/symbols/test_symbolSENTENCE.py b/tests/symbols/test_symbolSENTENCE.py index 1a466cf7e..470c12b16 100644 --- a/tests/symbols/test_symbolSENTENCE.py +++ b/tests/symbols/test_symbolSENTENCE.py @@ -1,9 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import unittest from unittest import TestCase -from StringIO import StringIO # Initialize import syspath import __init__ diff --git a/tests/symbols/test_symbolSTRING.py b/tests/symbols/test_symbolSTRING.py index 5f71a5b05..95d3b33f9 100644 --- a/tests/symbols/test_symbolSTRING.py +++ b/tests/symbols/test_symbolSTRING.py @@ -3,7 +3,6 @@ import unittest from unittest import TestCase -from StringIO import StringIO # Initialize import syspath import __init__ diff --git a/tests/symbols/test_symbolTYPECAST.py b/tests/symbols/test_symbolTYPECAST.py index f46e19517..ac8c0de40 100644 --- a/tests/symbols/test_symbolTYPECAST.py +++ b/tests/symbols/test_symbolTYPECAST.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- __autor__ = 'boriel' -import unittest from unittest import TestCase @@ -14,7 +13,7 @@ from symbols import VAR from symbols.type_ import Type from api.config import OPTIONS -from StringIO import StringIO +from six import StringIO from api.constants import CLASS @@ -81,7 +80,7 @@ def test_make_node_loose_byte3(self): t = TYPECAST.make_node(Type.ubyte, NUMBER(-3, lineno=1), lineno=2) self.assertEqual(self.OUTPUT, '') - def test_make_node_loose_byte3(self): + def test_make_node_loose_byte4(self): t = TYPECAST.make_node(Type.ubyte, NUMBER(-257, lineno=1), lineno=2) self.assertEqual(self.OUTPUT, "(stdin):1: warning: Conversion may lose significant digits\n") diff --git a/tests/symbols/test_symbolVARARRAY.py b/tests/symbols/test_symbolVARARRAY.py index 01cc0ed82..9cf6a959f 100644 --- a/tests/symbols/test_symbolVARARRAY.py +++ b/tests/symbols/test_symbolVARARRAY.py @@ -1,9 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import unittest from unittest import TestCase -from StringIO import StringIO # Initialize import syspath import __init__ @@ -13,6 +11,7 @@ from api.constants import CLASS from symbols.type_ import Type import symbols +import functools class TestSymbolVARARRAY(TestCase): @@ -39,7 +38,7 @@ def test_bounds(self): def test_count(self): arr = symbols.VARARRAY('test', self.bounds, 1) - self.assertEqual(arr.count, reduce(lambda x, y: x * y, (x.count for x in self.bounds))) + self.assertEqual(arr.count, functools.reduce(lambda x, y: x * y, (x.count for x in self.bounds))) def test_size(self): arr = symbols.VARARRAY('test', self.bounds, 1, type_=Type.ubyte) diff --git a/tools/update-rev b/tools/update-rev deleted file mode 100755 index 650cf0369..000000000 --- a/tools/update-rev +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# vim:ts=4:et - -FILE="version.py" -VERSION="1.4.0" -REV=$(git rev-list --all --count) - -echo "Updating $FILE to revision $REV..." -echo '#/usr/bin/env python' > $FILE -echo "VERSION = '$VERSION-s$REV'" >> $FILE - diff --git a/version.py b/version.py index 30e6b5a64..84262d36c 100755 --- a/version.py +++ b/version.py @@ -1,2 +1 @@ -#/usr/bin/env python -VERSION = '1.4.1997' +VERSION = '1.5.0' diff --git a/z80.py b/z80.py index 34537ef7e..c04f3aa00 100644 --- a/z80.py +++ b/z80.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim:ts=4:et: diff --git a/zxb.py b/zxb.py index 9d6ac3d06..b14e42299 100755 --- a/zxb.py +++ b/zxb.py @@ -8,7 +8,8 @@ import os import re from optparse import OptionParser -from StringIO import StringIO + +from six import StringIO import api.debug import api.optimize @@ -58,7 +59,7 @@ def output(memory, ofile=None): for m in memory: if len(m) > 0 and m[0] == '#': # Preprocessor directive? if ofile is None: - print m + print(m) else: ofile.write('%s\n' % m) continue @@ -66,12 +67,12 @@ def output(memory, ofile=None): # Prints a 4 spaces "tab" for non labels if ':' not in m: if ofile is None: - print ' ', + print(' '), else: ofile.write('\t') if ofile is None: - print m + print(m) else: ofile.write('%s\n' % m) diff --git a/zxbasm.py b/zxbasm.py index df1241232..53837ea52 100755 --- a/zxbasm.py +++ b/zxbasm.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: ts=4:et:sw=4 diff --git a/zxbasmpplex.py b/zxbasmpplex.py index db9c4f566..0b58d89e9 100755 --- a/zxbasmpplex.py +++ b/zxbasmpplex.py @@ -10,8 +10,7 @@ # This is the Lexer for the ZXBppASM (ZXBASM Preprocessor) # ---------------------------------------------------------------------- -import ply.lex as lex -import sys +from ply import lex import os from api.config import OPTIONS @@ -526,8 +525,6 @@ def __init__(self): while 1: tok = lexer.token() - if not tok: break - print tok - - - + if not tok: + break + print(tok) diff --git a/zxblex.py b/zxblex.py index 84e77cabd..f6b3df020 100755 --- a/zxblex.py +++ b/zxblex.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim:ts=4:et:sw=4 @@ -9,21 +9,20 @@ # the GNU General License # ---------------------------------------------------------------------- -import ply.lex as lex -import sys +from ply import lex from keywords import KEYWORDS as reserved def syntax_error(*args): - ''' Dummy function that will be called for invalid tokens. + """ Dummy function that will be called for invalid tokens. Should be overwritten. - ''' + """ pass ASM = '' # Set to asm block when commenting ASMLINENO = 0 # Line of ASM INLINE beginning -LABELS_ALLOWED = True # Whether numbers and IDs at beginning of line are taken as LABELS +LABELS_ALLOWED = True # Whether numbers and IDs at beginning of line are taken as LABELS IN_STATE = False # True if inside a state like "ASM" __STRING = '' # STRING Content __COMMENT_LEVEL = 0 # Nested comments level @@ -39,27 +38,24 @@ def syntax_error(*args): _tokens = ( 'NUMBER', 'PLUS', 'MINUS', 'MUL', 'DIV', 'POW', 'LP', 'RP', 'LT', 'LBRACE', 'RBRACE', - 'EQ', 'GT', 'LE', 'GE', 'NE', 'ID', + 'EQ', 'GT', 'LE', 'GE', 'NE', 'ID', 'NEWLINE', 'CO', 'SC', 'COMMA', 'STRC', 'RIGHTARROW', 'ADDRESSOF', 'LABEL') - preprocessor = { - 'line': '_LINE', - 'init': '_INIT', - 'require': '_REQUIRE', - 'pragma': '_PRAGMA', - 'push': '_PUSH', - 'pop': '_POP', - } + 'line': '_LINE', + 'init': '_INIT', + 'require': '_REQUIRE', + 'pragma': '_PRAGMA', + 'push': '_PUSH', + 'pop': '_POP', +} macros = ( '__LINE__', '__FILE__', - ) - - +) -tokens = _tokens + tuple(set(reserved.values())) + tuple(preprocessor.values()) + macros +tokens = sorted(_tokens + tuple(set(reserved.values())) + tuple(preprocessor.values()) + macros) def t_INITIAL_bin_comment_beginBlockComment(t): @@ -75,7 +71,7 @@ def t_comment_endBlockComment(t): global __COMMENT_LEVEL __COMMENT_LEVEL -= 1 - #if not __COMMENT_LEVEL: + # if not __COMMENT_LEVEL: # t.lexer.begin('INITIAL') t.lexer.pop_state() @@ -234,7 +230,7 @@ def t_INITIAL_SHARP(t): if find_column(t) == 1: t.lexer.begin('preproc') else: - t_INITIAL_string_asm_preproc_error(t) + t_INITIAL_bin_string_asm_preproc_comment_error(t) def t_LBRACE(t): @@ -284,7 +280,7 @@ def t_string_NGRAPH(t): global __STRING P = {' ': 0, "'": 2, '.': 8, ':': 10} - N = {' ': 0, "'": 1, '.': 4, ':': 5} + N = {' ': 0, "'": 1, '.': 4, ':': 5} __STRING += chr(128 + P[t.value[1]] + N[t.value[2]]) @@ -477,7 +473,7 @@ def t_preproc_INTEGER(t): def t_preproc_STRING(t): r'"[^"]*"' - t.value = t.value[1:-1] # Strip quotes + t.value = t.value[1:-1] # Strip quotes return t @@ -535,12 +531,12 @@ def t_OCTAL(t): t.value = t.value[:-1] t.type = 'NUMBER' t.value = int(t.value, 8) - + return t def t_BIN(t): - r'(%[01]+)|([01]+[bB])' # A Binary integer + r'(%[01]+)|([01]+[bB])' # A Binary integer # Note 00B is a 0 binary, but # 00Bh is a 12 in hex. So this pattern must come # after HEXA @@ -557,7 +553,7 @@ def t_BIN(t): def t_bin_NUMBER(t): - r'[01]+' # A binary integer + r'[01]+' # A binary integer t.value = int(t.value, 2) t.lexer.begin('INITIAL') @@ -610,10 +606,10 @@ def t_INITIAL_bin_string_asm_preproc_comment_error(t): # --------- END OF Token rules --------- def find_column(token): - ''' Compute column: + """ Compute column: input is the input text string token is a token instance - ''' + """ i = token.lexpos input = token.lexer.lexdata @@ -627,13 +623,13 @@ def find_column(token): def is_label(token): - ''' Return whether the token is a label (an integer number or id + """ Return whether the token is a label (an integer number or id at the beginning of a line. To do so, we compute find_column() and moves back to the beginning of the line if previous chars are spaces or tabs. If column 0 is reached, it's a label. - ''' + """ if not LABELS_ALLOWED: return False @@ -655,15 +651,15 @@ def is_label(token): return column == 1 -lexer = lex.lex() +lexer = lex.lex(lextab='zxblextab') -if __name__ == '__main__': # For testing purposes +if __name__ == '__main__': # For testing purposes import sys lexer.input(open(sys.argv[1], 'rt').read()) while 1: tok = lexer.token() - if not tok: break - print tok - + if not tok: + break + print(tok) diff --git a/zxbparser.py b/zxbparser.py index a40411946..80e631ebb 100755 --- a/zxbparser.py +++ b/zxbparser.py @@ -8,6 +8,7 @@ # This program is Free Software and is released under the terms of # the GNU General License # ---------------------------------------------------------------------- + import sys # PI Constant # PI = 3.1415927 is ZX Spectrum PI representation @@ -48,7 +49,6 @@ from backend import REQUIRES from zxblex import tokens # analysis:ignore -- Needed for PLY. Do not remove. - # ---------------------------------------------------------------------- # Function level entry ID in which scope we are into. If the list # is empty, we are at global scope @@ -87,6 +87,7 @@ # ---------------------------------------------------------------------- PRINT_IS_USED = False + # ---------------------------------------------------------------------- # "Macro" functions. Just return more complex expresions # ---------------------------------------------------------------------- @@ -96,6 +97,7 @@ def _TYPE(type_): ''' return SYMBOL_TABLE.basic_types[type_] + # ---------------------------------------------------------------------- # Wrapper functions to make AST nodes # ---------------------------------------------------------------------- @@ -115,9 +117,6 @@ def make_typecast(type_, node, lineno): ''' Wrapper: returns a Typecast node ''' assert isinstance(type_, symbols.TYPE) - # if not isinstance(type_, symbols.TYPE): - # type_ = make_type(TYPE.to_string(type_), lineno) - return symbols.TYPECAST.make_node(type_, node, lineno) @@ -283,7 +282,7 @@ def make_call(id_, lineno, args): if len(args) == 1: return symbols.STRSLICE.make_node(lineno, entry, args[0].value, - args[0].value) + args[0].value) entry.accessed = True return entry @@ -330,7 +329,6 @@ def make_label(id_, lineno): return SYMBOL_TABLE.declare_label(id_, lineno) - # ---------------------------------------------------------------------- # Operators precedence # ---------------------------------------------------------------------- @@ -364,7 +362,7 @@ def p_start(p): if PRINT_IS_USED: zxbpp.ID_TABLE.define('___PRINT_IS_USED___', 1) - #zxbasmpp.ID_TABLE.define('___PRINT_IS_USED___', 1) + # zxbasmpp.ID_TABLE.define('___PRINT_IS_USED___', 1) if zxblex.IN_STATE: p.type = 'NEWLINE' @@ -554,12 +552,13 @@ def p_arr_decl_initialized(p): | DIM ID LP bound_list RP typedef EQ const_vector NEWLINE | DIM ID LP bound_list RP typedef EQ const_vector CO ''' + def check_bound(boundlist, remaining): ''' Checks if constant vector bounds matches the array one ''' if not boundlist: # Returns on empty list if not isinstance(remaining, list): - return True # It's OK :-) + return True # It's OK :-) syntax_error(p.lineno(9), 'Unexpected extra vector dimensions. It should be %i' % len(remaining)) @@ -570,7 +569,7 @@ def check_bound(boundlist, remaining): if len(remaining) != boundlist[0].count: syntax_error(p.lineno(9), 'Mismatched vector size. Expected %i elements, got %i.' % (boundlist[0].count, len(remaining))) - return False # It's wrong. :-( + return False # It's wrong. :-( for row in remaining: if not check_bound(boundlist[1:], row): @@ -604,7 +603,7 @@ def p_bound(p): ''' bound : expr ''' p[0] = make_bound(make_number(OPTIONS.array_base.value, - lineno=p.lineno(1)), p[1], p.lexer.lineno) + lineno=p.lineno(1)), p[1], p.lexer.lineno) def p_bound_to_bound(p): @@ -853,17 +852,17 @@ def p_assignment(p): if variable.memsize != q[1].memsize: syntax_error(p.lineno(i), "Arrays '%s' and '%s' must have the same size" % - (variable.name, q[1].name)) + (variable.name, q[1].name)) return if variable.count != q[1].count: warning(p.lineno(i), "Arrays '%s' and '%s' don't have the same number of dimensions" % - (variable.name, q[1].name)) + (variable.name, q[1].name)) else: for b1, b2 in zip(variable.bounds, q[1].bounds): if b1.count != b2.count: warning(p.lineno(i), "Arrays '%s' and '%s' don't have the same dimensions" % - (variable.name, q[1].name)) + (variable.name, q[1].name)) break # Array copy @@ -908,12 +907,12 @@ def p_arr_assignment(p): return # There where errors p[0] = None - #api.check.check_is_declared_strict(p.lineno(i - 1), q[0], classname='array') + # api.check.check_is_declared_strict(p.lineno(i - 1), q[0], classname='array') entry = SYMBOL_TABLE.access_call(q[0], p.lineno(i - 1)) if entry is None: - #variable = SYMBOL_TABLE.make_var(q[0], p.lineno(1), TYPE.string) - #entry = SYMBOL_TABLE.get_id_entry(q[0]) + # 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: @@ -1398,7 +1397,7 @@ def p_do_loop(p): q = p[2] if p[1] == 'DO': - gl.LOOPS.append(('DO', )) + gl.LOOPS.append(('DO',)) if q is None: warning(p.lineno(1), 'Infinite empty loop') @@ -1424,7 +1423,7 @@ def p_do_loop_until(p): r = p[4] if p[1] == 'DO': - gl.LOOPS.append(('DO', )) + gl.LOOPS.append(('DO',)) p[0] = make_sentence('DO_UNTIL', r, q) @@ -1451,7 +1450,7 @@ def p_do_loop_while(p): r = p[4] if p[1] == 'DO': - gl.LOOPS.append(('DO', )) + gl.LOOPS.append(('DO',)) p[0] = make_sentence('DO_WHILE', r, q) gl.LOOPS.pop() @@ -1503,7 +1502,7 @@ def p_do_while_start(p): | DO WHILE expr NEWLINE ''' p[0] = p[3] - gl.LOOPS.append(('DO', )) + gl.LOOPS.append(('DO',)) def p_do_until_start(p): @@ -1511,14 +1510,14 @@ def p_do_until_start(p): | DO UNTIL expr NEWLINE ''' p[0] = p[3] - gl.LOOPS.append(('DO', )) + gl.LOOPS.append(('DO',)) def p_do_start(p): ''' do_start : DO CO | DO NEWLINE ''' - gl.LOOPS.append(('DO', )) + gl.LOOPS.append(('DO',)) def p_label_end_while(p): @@ -1568,7 +1567,7 @@ def p_while_start(p): ''' while_start : WHILE expr ''' p[0] = p[2] - gl.LOOPS.append(('WHILE', )) + gl.LOOPS.append(('WHILE',)) def p_exit(p): @@ -2604,7 +2603,7 @@ def p_function_body(p): def p_type_def_empty(p): ''' typedef : - ''' # Epsilon. Defaults to float + ''' # Epsilon. Defaults to float p[0] = make_type(_TYPE(gl.DEFAULT_TYPE).name, p.lexer.lineno, implicit=True) @@ -2633,6 +2632,7 @@ def p_preprocessor_line(p): ''' preproc_line : preproc_line_line NEWLINE ''' + def p_preprocessor_line_line(p): ''' preproc_line_line : _LINE INTEGER ''' @@ -2691,8 +2691,8 @@ def p_expr_usr(p): p[0] = make_builtin(p.lineno(1), 'USR_STR', p[2], type_=TYPE.uinteger) else: p[0] = make_builtin(p.lineno(1), 'USR', - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - type_=TYPE.uinteger) + make_typecast(TYPE.uinteger, p[2], p.lineno(1)), + type_=TYPE.uinteger) def p_expr_rnd(p): @@ -2706,24 +2706,24 @@ def p_expr_peek(p): ''' expr : PEEK expr %prec UMINUS ''' p[0] = make_builtin(p.lineno(1), 'PEEK', - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - type_=TYPE.ubyte) + make_typecast(TYPE.uinteger, p[2], p.lineno(1)), + type_=TYPE.ubyte) def p_expr_peektype_(p): ''' expr : PEEK LP numbertype COMMA expr RP ''' p[0] = make_builtin(p.lineno(1), 'PEEK', - make_typecast(TYPE.uinteger, p[5], p.lineno(4)), - type_=p[3]) + make_typecast(TYPE.uinteger, p[5], p.lineno(4)), + type_=p[3]) def p_expr_in(p): ''' expr : IN expr %prec UMINUS ''' p[0] = make_builtin(p.lineno(1), 'IN', - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - type_=TYPE.ubyte) + make_typecast(TYPE.uinteger, p[2], p.lineno(1)), + type_=TYPE.ubyte) def p_expr_lbound(p): @@ -2818,8 +2818,8 @@ def p_str(p): p[0] = symbols.STRING(str(p[3].value), p.lineno(1)) else: p[0] = make_builtin(p.lineno(1), 'STR', - make_typecast(TYPE.float_, p[3], p.lineno(2)), - type_=TYPE.string) + make_typecast(TYPE.float_, p[3], p.lineno(2)), + type_=TYPE.string) def p_inkey(p): @@ -2845,6 +2845,7 @@ def p_chr(p): def p_val(p): ''' expr : VAL expr %prec UMINUS ''' + def val(s): try: x = float(s) @@ -2863,6 +2864,7 @@ def val(s): def p_code(p): ''' expr : CODE expr %prec UMINUS ''' + def asc(x): if len(x): return ord(x[0]) @@ -2910,40 +2912,40 @@ 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)) + 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)) + 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)) + 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)) + 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)) + make_typecast(TYPE.float_, p[2], p.lineno(1)), + lambda x: math.atan(x)) # ---------------------------------------- @@ -2953,24 +2955,24 @@ 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)) + make_typecast(TYPE.float_, p[2], p.lineno(1)), + lambda x: math.exp(x)) def p_expr_logn(p): ''' expr : LN expr %prec UMINUS ''' p[0] = make_builtin(p.lineno(1), 'LN', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.log(x)) + make_typecast(TYPE.float_, p[2], p.lineno(1)), + lambda x: math.log(x)) def p_expr_sqrt(p): ''' expr : SQR expr %prec UMINUS ''' p[0] = make_builtin(p.lineno(1), 'SQR', - make_typecast(TYPE.float_, p[2], p.lineno(1)), - lambda x: math.sqrt(x)) + make_typecast(TYPE.float_, p[2], p.lineno(1)), + lambda x: math.sqrt(x)) # ---------------------------------------- @@ -3013,14 +3015,10 @@ def p_error(p): OPTIONS.stderr.value.write("%s\n" % msg) - - - # ---------------------------------------- # Initialization # ---------------------------------------- - -parser = yacc.yacc(method='LALR') +parser = yacc.yacc(method='LALR', tabmodule='zxbtab', debug=OPTIONS.Debug.value > 2) ast = None -data_ast = None # Global Variables AST +data_ast = None # Global Variables AST optemps = OpcodesTemps() diff --git a/zxbpp.py b/zxbpp.py index ea83d13f2..5e2a9e9a5 100755 --- a/zxbpp.py +++ b/zxbpp.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: ts=4:sw=4:et: @@ -18,7 +18,7 @@ import zxbpplex import zxbasmpplex from zxbpplex import tokens -import ply.yacc as yacc +from ply import yacc from api.config import OPTIONS from prepro.output import warning, error, CURRENT_FILE @@ -695,6 +695,7 @@ def main(argv): parser = yacc.yacc(method='LALR', tabmodule='zxbpptab') +parser.defaulted_states = {} ID_TABLE = DefinesTable() diff --git a/zxbpplex.py b/zxbpplex.py index 130c437ef..5ce92ecbf 100755 --- a/zxbpplex.py +++ b/zxbpplex.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- # ---------------------------------------------------------------------- @@ -10,12 +10,10 @@ # This is the Lexer for the ZXBpp (ZXBasic Preprocessor) # ---------------------------------------------------------------------- -import ply.lex as lex -import sys +from ply import lex import os from prepro.output import warning, error - EOL = '\n' # Names for std input/output @@ -24,44 +22,42 @@ STDERR = '' states = ( - ('prepro', 'exclusive'), - ('define', 'exclusive'), - ('defargsopt', 'exclusive'), - ('defargs', 'exclusive'), - ('defexpr', 'exclusive'), - ('pragma', 'exclusive'), - ('singlecomment', 'exclusive'), - ('comment', 'exclusive'), - ('asm', 'exclusive'), - ('asmcomment', 'exclusive'), - ('if', 'exclusive') - ) + ('prepro', 'exclusive'), + ('define', 'exclusive'), + ('defargsopt', 'exclusive'), + ('defargs', 'exclusive'), + ('defexpr', 'exclusive'), + ('pragma', 'exclusive'), + ('singlecomment', 'exclusive'), + ('comment', 'exclusive'), + ('asm', 'exclusive'), + ('asmcomment', 'exclusive'), + ('if', 'exclusive') +) _tokens = ('STRING', 'TOKEN', 'NEWLINE', '_ENDFILE_', 'FILENAME', 'ID', - 'INTEGER', 'EQ', 'PUSH', 'POP', 'LP', 'LLP', 'RRP', 'RP', 'COMMA', - 'CONTINUE', 'NUMBER', 'SEPARATOR', 'GT', 'GE', 'LT', 'LE', 'NE' - ) + 'INTEGER', 'EQ', 'PUSH', 'POP', 'LP', 'LLP', 'RRP', 'RP', 'COMMA', + 'CONTINUE', 'NUMBER', 'SEPARATOR', 'GT', 'GE', 'LT', 'LE', 'NE' + ) reserved_directives = { - 'include' : 'INCLUDE', - 'once' : 'ONCE', - 'define' : 'DEFINE', - 'undef' : 'UNDEF', - 'if' : 'IF', - 'ifdef' : 'IFDEF', - 'ifndef' : 'IFNDEF', - 'else' : 'ELSE', + 'include': 'INCLUDE', + 'once': 'ONCE', + 'define': 'DEFINE', + 'undef': 'UNDEF', + 'if': 'IF', + 'ifdef': 'IFDEF', + 'ifndef': 'IFNDEF', + 'else': 'ELSE', 'endif': 'ENDIF', - 'init' : 'INIT', - 'line' : 'LINE', + 'init': 'INIT', + 'line': 'LINE', 'require': 'REQUIRE', 'pragma': 'PRAGMA', - } - +} # List of token names. -tokens = _tokens + tuple(reserved_directives.values()) - +tokens = sorted(_tokens + tuple(reserved_directives.values())) class Lexer(object): @@ -74,7 +70,6 @@ def t_INITIAL_COMMENT(self, t): r"(\b[Rr][Ee][Mm]\b)|'" t.lexer.push_state('singlecomment') - def t_INITIAL_asmBegin(self, t): r'\b[aA][sS][mM]\b' t.type = 'TOKEN' @@ -82,7 +77,6 @@ def t_INITIAL_asmBegin(self, t): return t - def t_asm_asmEnd(self, t): r'\b[Ee][Nn][Dd][ \t]+[Aa][Ss][Mm]\b' t.type = 'TOKEN' @@ -90,24 +84,20 @@ def t_asm_asmEnd(self, t): return t - def t_asm_CONTINUE(self, t): r'[\\_]([ \t]*;.*)?\r?\n' t.lexer.lineno += 1 return t - def t_asm_COMMENT(self, t): r';' t.lexer.push_state('asmcomment') - def t_asmcomment_skip(self, t): r'.' pass - def t_asmcomment_NEWLINE(self, t): r'\r?\n' # New line => remove whatever state in top of the stack and replace it with INITIAL @@ -116,7 +106,6 @@ def t_asmcomment_NEWLINE(self, t): return t - def t_asm_NEWLINE(self, t): r'\r?\n' # New line => remove whatever state in top of the stack and replace it with INITIAL @@ -124,32 +113,27 @@ def t_asm_NEWLINE(self, t): return t - def t_asm_ID(self, t): r'[_A-Za-z][_A-Za-z0-9]*' return t - def t_asm_CHAR(self, t): r"'([^'\n]|'')'" t.type = 'TOKEN' return t - def t_asm_TOKEN(self, t): r"[][',.:$()*/+-]" return t - def t_INITIAL_ID(self, t): r'[_a-zA-Z][_a-zA-Z0-9]*[$%]?' return t - def t_prepro_define_defargs_defargsopt_defexpr_pragma_if_NEWLINE(self, t): r'\r?\n' t.lexer.lineno += 1 @@ -157,27 +141,23 @@ def t_prepro_define_defargs_defargsopt_defexpr_pragma_if_NEWLINE(self, t): return t - def t_INITIAL_NEWLINE(self, t): r'\r?\n' t.lexer.lineno += 1 return t - def t_prepro_define_defargs_defargsopt_defexpr_pragma_COMMENT(self, t): r"'" t.lexer.begin('singlecomment') - def t_singlecomment_NEWLINE(self, t): r'\r?\n' - t.lexer.pop_state() # Back to initial + t.lexer.pop_state() # Back to initial t.lexer.lineno += 1 return t - def t_singlecomment_prepro_define_pragma_defargs_defargsopt_CONTINUE(self, t): r'[_\\]\r?\n' t.lexer.lineno += 1 @@ -186,33 +166,28 @@ def t_singlecomment_prepro_define_pragma_defargs_defargsopt_CONTINUE(self, t): return t - def t_INITIAL_comment_beginBlock(self, t): r"/'" self.__COMMENT_LEVEL += 1 t.lexer.begin('comment') - def t_comment_NEWLINE(self, t): r'\r?\n' t.lexer.lineno += 1 return t - def t_comment_endBlock(self, t): r"'/" self.__COMMENT_LEVEL -= 1 if not self.__COMMENT_LEVEL: t.lexer.begin('INITIAL') - # Any other character is ignored until EOL def t_singlecomment_comment_Skip(self, t): r'.' pass - # Allows line breaking def t_defexpr_CONTINUE(self, t): r'[\\_]\r?\n' @@ -221,50 +196,42 @@ def t_defexpr_CONTINUE(self, t): return t - def t_prepro_pragma_defargs_define_if_skip(self, t): r'[ \t]+' - pass # Ignore whitespaces and tabs - + pass # Ignore whitespaces and tabs def t_if_EQ(self, t): r'==' return t - def t_if_NE(self, t): r'!=|<>' return t - def t_if_GE(self, t): r'>=' return t - def t_if_GT(self, t): r'>' return t - def t_if_LE(self, t): r'<=' return t - def t_if_LT(self, t): r'<' return t - def t_prepro_ID(self, t): - r'[_a-zA-Z][_a-zA-Z0-9]*' # preprocessor directives + 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') @@ -282,46 +249,39 @@ def t_prepro_ID(self, t): return t - def t_pragma_LP(self, t): r'\(' return t - def t_pragma_RP(self, t): r'\)' return t - def t_INITIAL_defexpr_if_LLP(self, t): r'\(' return t - def t_INITIAL_defexpr_if_RRP(self, t): r'\)' return t - def t_pragma_ID(self, t): - r'[_a-zA-Z][_a-zA-Z0-9]*' # pragma directives + r'[_a-zA-Z][_a-zA-Z0-9]*' # pragma directives if t.value.upper() in ('PUSH', 'POP'): t.type = t.value.upper() return t - def t_defargsopt_LP(self, t): r'\(' t.lexer.begin('defargs') return t - # Any other char than '(' means no arglist def t_defargsopt_TOKEN(self, t): r'[ \t)@,{}:;.+*/!|&~$-]|=>|<=|<>|=|<|>' @@ -329,109 +289,92 @@ def t_defargsopt_TOKEN(self, t): return t - def t_defargsopt_STRING(self, t): - r'"([^"\n]|"")*"' # a doubled quoted string + r'"([^"\n]|"")*"' # a doubled quoted string t.lexer.begin('defexpr') return t - def t_defargs_LP(self, t): r'\(' return t - def t_defargs_RP(self, t): r'\)' t.lexer.begin('defexpr') return t - def t_defargsopt_defexpr_ID(self, t): - r'[_a-zA-Z][_a-zA-Z0-9]*' # preprocessor directives + r'[_a-zA-Z][_a-zA-Z0-9]*' # preprocessor directives t.lexer.begin('defexpr') return t - def t_defargsopt_SEPARATOR(self, t): r'[ \t]+' t.lexer.begin('defexpr') - def t_defargs_if_ID(self, t): - r'[_a-zA-Z][_a-zA-Z0-9]*' # preprocessor directives + r'[_a-zA-Z][_a-zA-Z0-9]*' # preprocessor directives return t - def t_define_ID(self, t): r'[_a-zA-Z][_a-zA-Z0-9]*' # preprocessor directives t.lexer.begin('defargsopt') return t - def t_prepro_pragma_INTEGER(self, t): r'[0-9]+' # an integer number return t - def t_prepro_pragma_STRING(self, t): r'"([^"\n]|"")*"' # a doubled quoted string t.value = t.value[1:-1] # Remove quotes return t - def t_INITIAL_defexpr_asm_STRING(self, t): r'"([^"\n]|"")*"' # a doubled quoted string return t - def t_pragma_EQ(self, t): r'=' return t - def t_INITIAL_defexpr_defargs_prepro_COMMA(self, t): r',' return t - def t_INITIAL_asm_sharp(self, t): - r'[ \t]*\#' # Only matches if at beginning of line and "#" + r'[ \t]*\#' # Only matches if at beginning of line and "#" if self.find_column(t) == 1: t.lexer.push_state('prepro') # Start preprocessor self.expectingDirective = True - def t_INITIAL_defexpr_TOKEN(self, t): r'=>|<=|>=|<>|[$!&|~@:;{}.<>^=+*/%-]' return t - def t_INITIAL_CONTINUE(self, t): r'[\\_]\r?\n' t.lexer.lineno += 1 return t - def t_INITIAL_defexpr_asm_SEPARATOR(self, t): r'[ \t]+' return t - def t_INITIAL_defexpr_asm_HEXA(self, t): r'([0-9][0-9a-fA-F]*[hH])|(\$[0-9a-fA-F]+)' # Hexadecimal numbers @@ -439,9 +382,8 @@ def t_INITIAL_defexpr_asm_HEXA(self, t): return t - def t_INITIAL_defexpr_asm_BIN(self, t): - r'(%[01]+)|([01]+[bB])' # A Binary integer + r'(%[01]+)|([01]+[bB])' # A Binary integer # Note 00B is a 0 binary, but # 00Bh is a 12 in hex. So this pattern must come # after HEXA @@ -449,33 +391,29 @@ def t_INITIAL_defexpr_asm_BIN(self, t): return t - def t_INITIAL_defexpr_asm_if_NUMBER(self, t): # This pattern must come AFTER t_HEXA and t_BIN r'(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))([eE][-+]?[0-9]+)?' return t - def t_prepro_FILENAME(self, t): r'<[^>]*>' - t.value = t.value[1:-1] # Remove quotes + 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): + def t_INITIAL_defargs_defargsopt_prepro_define_defexpr_pragma_comment_singlecomment_asm_asmcomment_if_error(self, + t): ''' error handling rule ''' self.error("illegal preprocessor character '%s'" % t.value[0]) - 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) - def include(self, filename): ''' Changes FILENAME and line count ''' @@ -508,7 +446,6 @@ def include(self, filename): return result - def include_end(self): ''' Performs and end of include. ''' @@ -516,21 +453,20 @@ def include_end(self): self.input_data = self.filestack[-1][3] self.filestack.pop() - if self.filestack == []: # End of input? + if self.filestack == []: # End of input? return - self.filestack[-1][1] += 1 # Increment line counter of previous file + self.filestack[-1][1] += 1 # Increment line counter of previous file result = lex.LexToken() - result.value = self.put_current_line(suffix = '\n') + result.value = self.put_current_line(suffix='\n') result.type = '_ENDFILE_' result.lineno = self.lex.lineno result.lexpos = self.lex.lexpos 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]) @@ -539,7 +475,6 @@ def input(self, str, filename = ''): self.lex = lex.lex(object=self) self.lex.input(self.input_data) - def token(self): ''' Returns a token from the current input. If tok is None from the current input, it means we are at end of current input @@ -570,7 +505,6 @@ def token(self): return tok - def find_column(self, token): ''' Compute column: - token is a token instance @@ -585,20 +519,17 @@ def find_column(self, token): return column - def error(self, msg): ''' Prints an error msg, and exits. ''' error(self.lex.lineno, msg) sys.exit(1) - def warning(self, msg): ''' Emmits a warning and continue execution. ''' warning(self.lex.lineno, msg) - def __init__(self): ''' Creates a new GLOBAL lexer instance ''' @@ -607,13 +538,11 @@ def __init__(self): self.input_data = '' self.tokens = tokens self.states = states - self.next_token = None # if set to something, this will be returned once + self.next_token = None # if set to something, this will be returned once self.expectingDirective = False # True if the lexer expects a preprocessor directive self.__COMMENT_LEVEL = 0 - - # --------------------- PREPROCESOR FUNCTIONS ------------------- # Needed for states @@ -628,8 +557,6 @@ def __init__(self): while 1: tok = lexer.token() - if not tok: break - print tok - - - + if not tok: + break + print(tok)