DEFFNder

Description of the page content

Library of Sinclair BASIC functions written in Z80.

Tags:

DEFFNder is a small library of Sinclair BASIC functions written in Z80. So far there are 20 functions in it. I wrote them for a Sinclair BASIC project. Four of them are based on code published on Microhobby in 1986 and 1987, mainly by Ricardo Serrial Wigge (full details in the sources). Five of them (and the demo) were later written and contributed by Derek Bolli.

Some of the functions are generic and can be found in more powerful versions of BASIC. Other functions are very specific, especially written for my project.

The source code is fully documented, in order to help one to understand how every function works, how to modify the functions and how to write new ones.

The code of the functions is relocatable and ready to be used. More details are in the included README.adoc file.

Source code

; fn_band16.z80s 
;
; Bitwise AND 16 bit BASIC function for ZX Spectrum

; Version B-00-20151222

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)
; Portions Copyright (C) 2015 Derek Bolli (dbolli at bigpond dot net dot au)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a bitwise AND 16 bit function to Sinclair
; BASIC, that ANDs a 16-bit value with a second 16-bit value and
; returns the result.

; --------------------------------------------------------------
; Usage

; A function must be defined this way.
; (the actual names are unimportant):
;   10 DEF FN a(a,b)=USR band16
; Where:
;   a     = word 1 operand
;   b     = word 2 operand
;   band16 = address of the machine code routine

; Example:
;   LET n = FN a(43690,65287)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN d(a) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     a(
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     ,
; +08     b
; +09     14
; +10     0
; +11     [sign byte: 0=positive; 255=negative]
; +12     [16-bit number, LSB first]
; +14     0
; +15     )=

; --------------------------------------------------------------
; History

; 2015-12-22: Written. Version B-00.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_band16,fn_band16_size
fn_band16:

  local DEFADD
DEFADD equ 23563 ; system variable

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = word 1
  ld e,(ix+12)
  ld d,(ix+13)
  ; de = word 2
  ld a,l
  and e
  ld c,a
  ; c = LSB of result
  ld a,h
  and d
  ld b,a
  ; b = MSB of result
  ret

fn_band16_size equ $-fn_band16

  endp

; vim: textwidth=64

; fn_bor16.z80s 
;
; Bitwise OR 16 bit BASIC function for ZX Spectrum

; Version B-00-20151222

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)
; Portions Copyright (C) 2015 Derek Bolli (dbolli at bigpond dot net dot au)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a bitwise OR 16 bit function to Sinclair BASIC,
; that ORs a 16-bit value with a second 16-bit value and returns
; the result.

; --------------------------------------------------------------
; Usage

; A function must be defined this way.
; (the actual names are unimportant):
;   10 DEF FN r(a,b)=USR bor16
; Where:
;   a     = word 1 operand
;   b     = word 2 operand
;   bor16 = address of the machine code routine

; Example:
;   LET n = FN r(43520,65450)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN d(a) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     r(
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     ,
; +08     b
; +09     14
; +10     0
; +11     [sign byte: 0=positive; 255=negative]
; +12     [16-bit number, LSB first]
; +14     0
; +15     )=

; --------------------------------------------------------------
; History

; 2015-12-22: Written. Version B-00.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_bor16,fn_bor16_size
fn_bor16:

  local DEFADD
DEFADD equ 23563 ; system variable

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = word 1
  ld e,(ix+12)
  ld d,(ix+13)
  ; de = word 2
  ld a,l
  or e
  ld c,a
  ; c = LSB of result
  ld a,h
  or d
  ld b,a
  ; b = MSB of result
  ret

fn_bor16_size equ $-fn_bor16

  endp

; vim: textwidth=64

; fn_bxor16.z80s 
;
; Bitwise XOR 16 bit BASIC function for ZX Spectrum

; Version B-00-20151222

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)
; Portions Copyright (C) 2015 Derek Bolli (dbolli at bigpond dot net dot au)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a bitwise XOR 16 bit function to Sinclair BASIC, that XORs a 16-bit
; value with a second 16-bit value and returns the result.

; --------------------------------------------------------------
; Usage

; A function must be defined this way.
; (the actual names are unimportant):
;   10 DEF FN x(a,b)=USR bxor16
; Where:
;   a     = word 1 operand
;   b     = word 2 operand
;   bxor16 = address of the machine code routine

; Example:
;   LET n = FN x(43520,65450)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN d(a) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     x(
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     ,
; +08     b
; +09     14
; +10     0
; +11     [sign byte: 0=positive; 255=negative]
; +12     [16-bit number, LSB first]
; +14     0
; +15     )=

; --------------------------------------------------------------
; History

; 2015-12-22: Written. Version B-00.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_bxor16,fn_bxor16_size
fn_bxor16:

  local DEFADD
DEFADD equ 23563 ; system variable

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = word 1
  ld e,(ix+12)
  ld d,(ix+13)
  ; de = word 2
  ld a,l
  xor e
  ld c,a
  ; c = LSB of result
  ld a,h
  xor d
  ld b,a
  ; b = MSB of result
  ret

fn_bxor16_size equ $-fn_bxor16

  endp

; vim: textwidth=64

; fn_dispbin.z80s  
;
; Display Binary BASIC function for ZX Spectrum

; Version B-00-20151223

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)
; Portions Copyright (C) 2015 Derek Bolli (dbolli at bigpond dot net dot au)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a Display Binary function to Sinclair BASIC,
; that prints the 8-bit value passed as an operand as an 8 digit
; binary value.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this way
; (the actual names are unimportant):
;   10 DEF FN b$(b)="" AND USR dispbin
; Where:
;   b     = 8-bit binary number to display
;   dispbin = address of the machine code routine

; Example:
;   PRINT FN b$(BIN 01010101)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN s$(s$,n) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     b$(
; +00     n
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     )=

; --------------------------------------------------------------
; History of this file

; 2015-12-23: Written. Version B-00.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_dispbin,fn_dispbin_size
fn_dispbin:

  local exit

; ..............................
; System variables

  local DEFFADD
DEFADD equ 23563

; ..............................
; ROM routines

  local STK_STORE
STK_STORE   equ 0x2ab6
            ; Input:
            ;   A = flag
            ;   DE = string address
            ;   BC = string length

  local STK_FETCH
STK_FETCH   equ 0x2bf1
            ; Output:
            ;   A = flag
            ;   DE = string address
            ;   BC = string length

; ..............................
; Start

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = number to display
  ld a,l

  push af

  xor a
  ld bc,8
  ; a = 0 (=new string)
  ; de = string address, undetermined
  ; bc = length of the new string
  call STK_STORE
  rst 0x28 ; calculator
  db 0x17 ; concatenate to the empty string in the DEF FN expression
  db 0x38 ; end
  call STK_FETCH
  call STK_STORE
  ; de = result string address
  ; bc = result string length

  pop af

dispbin:
  and a
  ld b,8
dispblp1:
  rl a
  push af
  ld a,'0'
  jr nc,dispbnc1
  ld a,'1'
dispbnc1:
  ld (de),a
  inc de
  pop af
  djnz dispblp1

; ..............................
exit:

  ld bc,1
  ret

fn_dispbin_size equ $-fn_dispbin

  endp

; vim: textwidth=64

; fn_disphex.z80s  
;
; Display Hex BASIC function for ZX Spectrum

; Version B-00-20151227

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)
; Portions Copyright (C) 2015 Derek Bolli (dbolli at bigpond dot net dot au)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a Display Hex function to Sinclair BASIC, that
; prints the 8-bit value passed as an operand as a 2 digit hex
; value.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this way
; (the actual names are unimportant):
;   10 DEF FN h$(n)="" AND USR disphex
; Where:
;   n     = 8-bit binary number to display
;   disphex = address of the machine code routine

; Example:
;   PRINT FN h$(255)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN s$(s$,n) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     h$(
; +00     n
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     )=

; --------------------------------------------------------------
; History of this file

; 2015-12-27: Written. Version B-00.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_disphex,fn_disphex_size
fn_disphex:

  local exit

; ..............................
; System variables

  local DEFFADD
DEFADD equ 23563

; ..............................
; ROM routines

  local STK_STORE
STK_STORE   equ 0x2ab6
            ; Input:
            ;   A = flag
            ;   DE = string address
            ;   BC = string length

  local STK_FETCH
STK_FETCH   equ 0x2bf1
            ; Output:
            ;   A = flag
            ;   DE = string address
            ;   BC = string length

; ..............................
; Start

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = number to display
  ld a,l

  push af

  xor a
  ld bc,2
  ; a = 0 (=new string)
  ; de = string address, undetermined
  ; bc = length of the new string
  call STK_STORE
  rst 0x28 ; calculator
  db 0x17 ; concatenate to the empty string in the DEF FN expression
  db 0x38 ; end
  call STK_FETCH
  call STK_STORE
  ; de = result string address
  ; bc = result string length

  pop af

dumphex:
  push af
  ; Dump two hex digits in A to (DE) and (DE+1)
  rrca
  rrca
  rrca
  rrca
  and $0f
  ; %00001111
  cp $0a
  jr c,dmphnc1
  add a,7
dmphnc1:
  add a,$30
  ld (de),a
  ; Store hex digit at (DE)
  inc de
  ; Increment destination pointer
  pop af
  and $0f
  ; %00001111
  cp $0a
  jr c,dmphnc2
  add a,7
dmphnc2:
  add a,$30
  ld (de),a
  ; Store hex digit at (DE)
  inc de
  ; Increment destination pointer
;  ret
  ; DE points to original DE + 2

; ..............................
exit:

  ld bc,1
  ret

fn_disphex_size equ $-fn_disphex

  endp

; vim: textwidth=64

; fn_dpeek.z80s 
;
; DPEEK BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a DPEEK function to Sinclair BASIC, that
; returns the 16-bit value from a memory address.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this
; way (the actual names are unimportant):
;   10 DEF FN d(a)=USR dpeek
; Where:
;   a     = address to peek
;   dpeek = address of the machine code routine

; Example:
;   PRINT FN d(1887)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN d(a) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     d
; -01     (
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     )
; +08     =

; --------------------------------------------------------------
; History of this file

; 2014-08-07: Written.
;
; 2014-08-10: Removed unnecessary saving and restoring of
; registers.  New: 'proc', 'local' and 'public', in order to
; make it possible to combine several modules into a single
; file.
;
; 2015-01-22: Some changes for publication. Version B-00.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_dpeek,fn_dpeek_size
fn_dpeek:

  local DEFADD
  DEFADD equ 23563 ; system variable

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = parameter address
  ld c,(hl)
  inc hl
  ld b,(hl)
  ; bc = value, returned by USR
  ret

fn_dpeek_size equ $-fn_dpeek

  endp

; vim: textwidth=64

; fn_dpoke.z80s 
;
; DPOKE BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a DPOKE function to Sinclair BASIC, that pokes
; a 16-bit value into a memory address.

; --------------------------------------------------------------
; Usage

; A function must be defined this way.
; (the actual names are unimportant):
;   10 DEF FN p(a,n)=USR dpoke
; Where:
;   a     = address to poke into
;   n     = 16-bit number to poke
;   dpoke = address of the machine code routine

; Example:
;   RANDOMIZE FN p(22528,1024)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN d(a) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     p(
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     ,
; +08     n
; +09     14
; +10     0
; +11     [sign byte: 0=positive; 255=negative]
; +12     [16-bit number, LSB first]
; +14     0
; +15     )=

; --------------------------------------------------------------
; History

; 2014-08-10: Written 
;
; 2015-01-22: Some changes for publication. Version B-00.
;
; 2015-02-25: Typo in "Internal".
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_dpoke,fn_dpoke_size
fn_dpoke:

  local DEFADD
DEFADD equ 23563 ; system variable

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = address
  ld e,(ix+12)
  ld d,(ix+13)
  ; de = number 
  ld (hl),e
  inc hl
  ld (hl),d
  ret

fn_dpoke_size equ $-fn_dpoke

  endp

; vim: textwidth=64

; fn_instr1.z80s
;
; Simplified INSTR BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Authors and license

; Copyright (C) 1986 Ricardo Serrial Wigge
;   1986-05, original version.
;   Published in:
;     Microhobby magazine, issue 77 (1986-05-06), pages 22-24:
;     http://microhobby.org/
;     http://microhobby.speccy.cz/mhf/077/MH077_22.jpg
;     http://microhobby.speccy.cz/mhf/077/MH077_23.jpg
;     http://microhobby.speccy.cz/mhf/077/MH077_24.jpg

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)
;   2014-08, modified version.

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this way
; (the actual names are unimportant):
;   10 DEF FN i(h$,n$)=USR instr1
; Where:
;   h$     = the haystack, the searched string
;   n$     = the needle, the string to be searched for
;   instr1 = address of the machine code routine

; Example:
;   PRINT FN i("En vilagxo de La Mancxo kies nomon mi ne...","Mancxo")

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN i(h$,n$) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     i
; -01     (
; +00     h
; +01     $
; +02     14
; +03     [string type]      
; +04     [string address, LSB first]
; +06     [string length, LSB first]
; +08     ,
; +09     n
; +10     $
; +11     14
; +12     [string type]      
; +13     [string address, LSB first]
; +15     [string length, LSB first]
; +17     )
; +18     =

; --------------------------------------------------------------
; History of this file

; 2014-08-06: This modified version doesn't use the position
; parameter. The search is done always from the start of the
; haystack.
;
; 2014-08-10: Removed unnecessary saving and restoring of
; registers.  New: 'proc', 'local' and 'public', in order to
; make it possible to combine several modules into a single
; file.
;
; 2015-01-22: Some typos fixed. Credits improved. Some changes
; for publication. Version B-00.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_instr1,fn_instr1_size
fn_instr1:

  local again
  local exit
  local found
  local haystack_exhausted
  local match
  local needle_exhausted
  local not_found

; ..............................
; System variables

  local DEFADD
DEFADD equ 23563

; ..............................
; Parameters

  ld ix,(DEFADD)
  ld a,(ix+6)
  or (ix+7) ; empty string?
  jr z, not_found

  ld e,(ix+4)
  ld d,(ix+5)
  ; de = address of h$
  ld l,(ix+6)
  ld h,(ix+7)
  ; hl = length of h$
  add hl,de
  ; hl = address after the end of h$

  ; Use ix+6 as temporary storage
  ld (ix+6),l
  ld (ix+7),h

  ld l,e
  ld h,d
  ; hl = address of h$

; ..............................
again:

  ; Start searching h$ for n$ from the current position of h$

  ld e,(ix+13)
  ld d,(ix+14)
  dec de
  ; de = address of n$ -1
  ld c,(ix+15)
  ld b,(ix+16)
  ; bc =length of n$

; ..............................
match:

  ; Keep on searching h$

  inc de
  ; de = address of current char of n$
  ; hl = address of current char of h$
  ld a,(de)
  cpi ; compare a with (hl) and increment hl
  ex af,af'
  push hl
  push de

  ld e,(ix+6)
  ld d,(ix+7)
  ; de = address after the end of h$
  and a
  sbc hl,de
  ; z = last char of h$?
  pop de
  pop hl
  jr z,haystack_exhausted

  ld a,b
  or c
  ; z = last char of n$?
  jr z,needle_exhausted

  ex af,af'
  ; z = (hl)=(de)?
  jr z,match
  jr again

; ..............................
needle_exhausted:

  ex af,af'
  ; z = (hl)=(de)?
  jr nz,again
  jr found

; ..............................
haystack_exhausted:

  ld a,b
  or c
  ; z = bc=0?
  jr nz,not_found

  ex af,af'
  ; z = (hl)=(de)?
  jr z,found

; ..............................
not_found:

  ld bc,0
  jr exit

; ..............................
found:

  ; hl = address of current char of h$
  ld e,(ix+4)
  ld d,(ix+5)
  ; de = address of h$
  and a
  sbc hl,de
  ; hl = offset to the current char of h$
  ld e,(ix+15)
  ld d,(ix+16)
  ; de = length of n$
  and a
  sbc hl,de
  inc hl
  ; hl = position of n$ in h$
  ld b,h
  ld c,l
  ; bc = position of n$ in h$

; ..............................
exit:

  ; bc = position of n$ in h$ (or zero)
  ret

fn_instr1_size equ $-fn_instr1

  endp

; vim: textwidth=64

; fn_instr.z80s
;
; INSTR BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Authors and license

; Copyright (C) 1986 Ricardo Serrial Wigge
;   The code was published in Microhobby magazine,
;   issue 77 (1986-05-06), pages 22-24:
;     http://microhobby.org/
;     http://microhobby.speccy.cz/mhf/077/MH077_22.jpg
;     http://microhobby.speccy.cz/mhf/077/MH077_23.jpg
;     http://microhobby.speccy.cz/mhf/077/MH077_24.jpg

; Copyright (C) 2014 Marcos Cruz (programandala.net)
;   Typed, commented and improved the code.

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this way
; (the actual names are unimportant):
;   10 DEF FN i(h$,n$,p)=USR instr
; Where:
;   h$    = the haystack, the searched string
;   n$    = the needle, the string to be searched for
;   p     = start search position in h$
;   instr = address of the machine code routine

; Example:
;   PRINT FN i("En vilagxo de La Mancxo kies nomon mi ne...","Mancxo",1)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN i(h$,n$,p) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     i
; -01     (
; +00     h
; +01     $
; +02     14
; +03     [string type]      
; +04     [string address, LSB first]
; +06     [string length, LSB first]
; +08     ,
; +09     n
; +10     $
; +11     14
; +12     [string type]      
; +13     [string address, LSB first]
; +15     [string length, LSB first]
; +17     ,
; +18     p
; +19     14
; +20     0
; +21     [sign byte: 0=positive; 255=negative]
; +22     [16-bit number, LSB first]
; +24     0
; +25     )
; +26     =

; --------------------------------------------------------------
; History of this file

; 2014-08-06:
;
; Typed from the magazine and compiled with Pasmo.  The code had
; useless NOP and DS.
;
; Problem: The FN function always returns 0, and somehow
; corrupts the STOP command, because it is ignored by the
; interpreter. The BREAK key causes the system to frozen.
;
; Fix: I modified the code: I saved the content of the UNUSED
; system variable, and restored it before returning. The strange
; behaviours ceased. It seems that variable is not so "unused"
; by the system, at least by ZX Spectrum 128K, or 48K with the
; gw03 ROM.
;
; Change: An optional method is provided: the parameter zone of
; the function is used as storage instead of UNUSED. Conditional
; compilation is used.
;
; 2014-08-10:
;
; Removed unnecessary saving and restoring of registers.
;
; New: 'proc', 'public' and 'local', in order to make it
; possible to combine several modules into a single file.
;
; 2015-01-22:
;
; Some typos fixed. Some changes for publication. Version B-00.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------
; To-do list

; Check if the start position is greater than the length of the
; haystack.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_instr,fn_instr_size
fn_instr:

  local again
  local exit
  local found
  local haystack_exhausted
  local match
  local needle_exhausted
  local not_found

; ..............................
; Options

; Use the system variable UNUSED as a temporary store?
; Otherwise, the address of the length of the h$ argument in DEF
; FN will be used.

  local USE_UNUSED
  USE_UNUSED equ 0

; If USE_UNUSED is true, do save the content of the UNUSED
; system variable? It seems saving it is not required only by
; the original ZX Spectrum 48K without interfaces.  Other models
; or certain interfaces may use this system variable.

  local SAVE_UNUSED
  SAVE_UNUSED equ 1

; ..............................
; System variables

  local DEFFADD
DEFADD equ 23563
  local UNUSED
UNUSED equ 23728

; ..............................
; Parameters

  ld ix,(DEFADD)
  ld a,(ix+6)
  or (ix+7) ; empty string?
  jr z, not_found

  if USE_UNUSED && SAVE_UNUSED

    ; Save the content of the UNUSED system variable

    ld hl,(UNUSED)
    ld (backup),hl

  endif

  ld e,(ix+4)
  ld d,(ix+5)
  ; de = address of h$
  ld l,(ix+6)
  ld h,(ix+7)
  ; hl = length of h$
  add hl,de
  ; hl = address after the end of h$

  if USE_UNUSED
    ld (UNUSED),hl
  else
    ld (ix+6),l
    ld (ix+7),h
  endif

  ld l,(ix+22)
  ld h,(ix+23)
  ; hl = p
  dec hl
  add hl,de
  ; hl = start address in h$

; ..............................
again:

  ; Start searching h$ for n$ from the current position of h$

  ld e,(ix+13)
  ld d,(ix+14)
  dec de
  ; de = address of n$ -1
  ld c,(ix+15)
  ld b,(ix+16)
  ; bc =length of n$

; ..............................
match:

  ; Keep on searching h$

  inc de
  ; de = address of current char of n$
  ; hl = address of current char of h$
  ld a,(de)
  cpi ; compare a with (hl) and increment hl
  ex af,af'
  push hl
  push de

  if USE_UNUSED
    ld de,(UNUSED)
  else
    ld e,(ix+6)
    ld d,(ix+7)
  endif
  ; de = address after the end of h$
  and a
  sbc hl,de
  ; z = last char of h$?
  pop de
  pop hl
  jr z,haystack_exhausted

  ld a,b
  or c
  ; z = last char of n$?
  jr z,needle_exhausted

  ex af,af'
  ; z = (hl)=(de)?
  jr z,match
  jr again

; ..............................
needle_exhausted:

  ex af,af'
  ; z = (hl)=(de)?
  jr nz,again
  jr found

; ..............................
haystack_exhausted:

  ld a,b
  or c
  ; z = bc=0?
  jr nz,not_found

  ex af,af'
  ; z = (hl)=(de)?
  jr z,found

; ..............................
not_found:

  ld bc,0
  jr exit

; ..............................
found:

  ; hl = address of current char of h$
  ld e,(ix+4)
  ld d,(ix+5)
  ; de = address of h$
  and a
  sbc hl,de
  ; hl = offset to the current char of h$
  ld e,(ix+15)
  ld d,(ix+16)
  ; de = length of n$
  and a
  sbc hl,de
  inc hl
  ; hl = position of n$ in h$
  ld b,h
  ld c,l
  ; bc = position of n$ in h$

; ..............................
exit:

  ; bc = position of n$ in h$ (or zero)

  if USE_UNUSED && SAVE_UNUSED

    backup equ $+1

    ld hl,0
    ld (UNUSED),hl

  endif

  ret

fn_instr_size equ $-fn_instr

  endp

; vim: textwidth=64

; fn_lookup16.z80s
;
; A database BASIC function for ZX Spectrum

; Version B-00-20150813

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a function to Sinclair BASIC that searchs a
; lookup table of 16-bit values, stored in a string, for a
; 16-bit key value, and returns its associated value.

; --------------------------------------------------------------
; Usage

; A BASIC function must be defined this way:
; (the actual names are unimportant):
;   10 DEF FN f(h$,k)=USR lookup16
; Where:
;   h$       = the haystack, the searched string,
;              with the following structure:
;              every 16-bit key precedes its associated
;              16-bit value; both are stored in two bytes
;              (chars of the string), in the usual Z80 format
;              (least significant byte first).
;   k        = the key to be searched for
;   lookup16 = address of the machine code routine

; Example for a text adventure, where a$ holds a lookup table of
; actions associated to line numbers:
;   GO TO FN f(a$,action)

; WARNING: As explained above, the haystack string must consist
; of groups of exactly 4 bytes: a 16-bit key followed by its
; 16-bit value. When the string is not well formed (for example,
; any single character is missing), the end check will fail and
; strange things will happen.

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN i(h$,n) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     f(
; +00     h$
; +02     14
; +03     [string type]      
; +04     [string address, LSB first]
; +06     [string length, LSB first]
; +08     ,
; +09     k
; +10     14
; +11     0
; +12     [sign byte: 0=positive; 255=negative]
; +13     [16-bit number, LSB first]
; +15     0
; +16     )=

; --------------------------------------------------------------
; History of this file

; 2015-02-28: First version (started with the code of
; <fn_termn.z80s>).
;
; 2015-03-01: Fix: Usage.
;
; 2015-08-10: Revision. License.
;
; 2015-08-13: Fixed typo in the usage instructions. Thanks Derek
; (http://www.worldofspectrum.org/forums/discussion/comment/831230/#Comment_831230).

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_lookup16,fn_lookup16_size
fn_lookup16:

  local check
  local next
  local found

; ..............................
; System variables

  local DEFADD
DEFADD equ 23563

; ..............................
; Parameters

  ld ix,(DEFADD)

  ld c,(ix+6)
  ld b,(ix+7)
  ; bc = len of h$
  ld a,b
  or c ; is h$ empty?
  ret z ; if so, back to BASIC (the function returns 0)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = start of h$
  ld e,(ix+13)
  ld d,(ix+14)
  ; de = k

; ..............................
check:

  ; Check the current key

  ; hl = address of the current key
  ; de = key searched for

  ld a,(hl)
  cp e
  inc hl
  jr nz,next
  ld a,(hl)
  cp d
  jr z,found

next:

  ; hl = address of the second byte of the current key
  ; bc = remaining length

  inc hl
  inc hl
  inc hl ; hl = address of the next key
  dec bc
  dec bc
  dec bc
  dec bc ; bc = remaining chars
  ld a,b
  or c ; end of the haystack?
  ret z ; if so, back to BASIC (the function returns 0)

  jr check

found:

  ; The key was found

  ; hl = address of the second byte of the current key

  inc hl
  ld c,(hl)
  inc hl
  ld b,(hl)
  ; bc =  value
  ret ; back to BASIC (the function returns the value)


fn_lookup16_size: equ $-fn_lookup16

  endp

; vim: textwidth=64

; fn_lookup8.z80s
;
; A database BASIC function for ZX Spectrum

; Version B-00-20150813

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a function to Sinclair BASIC that searchs a
; lookup table of 8-bit values, stored in a string, for a
; 8-bit key value, and returns its associated value.

; --------------------------------------------------------------
; Usage

; A BASIC function must be defined this way:
; (the actual names are unimportant):
;   10 DEF FN f(h$,k)=USR lookup8
; Where:
;   h$       = the haystack, the searched string,
;              with the following structure:
;              every 8-bit key precedes its associated
;              8-bit value.
;   k        = the key to be searched for
;   lookup8 = address of the machine code routine

; Example of a key translation table:
;   LET t$=CHR$ 200 + CHR$ 160 + CHR$ 203 + CHR$ 162
;   LET key=FN f(t$,key)

; WARNING: As explained above, the haystack string must consist
; of pairs of chars.  When the string is not well formed (for
; example, any single character is missing), the end check will
; fail and strange things will happen.

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN i(h$,n) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     f(
; +00     h$
; +02     14
; +03     [string type]      
; +04     [string address, LSB first]
; +06     [string length, LSB first]
; +08     ,
; +09     k
; +10     14
; +11     0
; +12     [sign byte: 0=positive; 255=negative]
; +13     [16-bit number, LSB first]
; +15     0
; +16     )=

; --------------------------------------------------------------
; History of this file

; 2015-03-01: First version (started with the code of
; <fn_lookup16.z80s>).
;
; 2015-08-10: Revision. License.
;
; 2015-08-13: Fixed typo in the usage instructions. Thanks Derek
; (http://www.worldofspectrum.org/forums/discussion/comment/831230/#Comment_831230).

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_lookup8,fn_lookup8_size
fn_lookup8:

  local check
  local next
  local found

; ..............................
; System variables

  local DEFADD
DEFADD equ 23563

; ..............................
; Parameters

  ld ix,(DEFADD)

  ld c,(ix+6)
  ld b,(ix+7)
  ; bc = len of h$
  ld a,b
  or c ; is h$ empty?
  ret z ; if so, back to BASIC (the function returns 0)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = start of h$
  ld e,(ix+13)
  ; e = k

; ..............................
check:

  ; Check the current key

  ; hl = address of the current key
  ; e = key searched for

  ld a,(hl)
  cp e
  inc hl
  jr z,found

next:

  ; hl = address of the current value
  ; bc = remaining length

  inc hl ; hl = address of the next key
  dec bc
  dec bc ; bc = remaining chars
  ld a,b
  or c ; end of the haystack?
  ret z ; if so, back to BASIC (the function returns 0)

  jr check

found:

  ; The key was found

  ; hl = address of the current value

  ld c,(hl)
  ld b,0
  ; bc =  value
  ret ; back to BASIC (the function returns the value)


fn_lookup8_size: equ $-fn_lookup8

  endp

; vim: textwidth=64

; fn_peekcs.z80s 
;
; PEEKC$ BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a PEEKC$ function to Sinclair BASIC. PEEKC$
; gets a counted string from memory. A counted string stores its
; length in its first byte (the length does not include the
; count byte itself).  This is the usual format used by the
; Forth language.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this
; way (the actual names are unimportant):
;   10 DEF FN p$(a)="" AND USR peekcs
; Where:
;   a      = address of the string (whose first byte holds the length)
;   peekcs = address of the machine code routine

; Example:
;   PRINT FN p$(filename)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN p$(a) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     p$(
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +08     )=

; --------------------------------------------------------------
; History of this file

; 2015-01-22: Written.
;
; 2015-02-10: Typo.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_peekcs,fn_peekcs_size
fn_peekcs:

; ..............................
; System variables

  local DEFFADD
DEFADD equ 23563

; ..............................
; ROM routines

  local STK_STORE
STK_STORE   equ 0x2ab6
            ; Input:
            ;   A = flag
            ;   DE = string address
            ;   BC = string length

; ..............................
; Start

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = counted string address
  ld c,(hl)
  ld b,0
  ; bc = string length
  inc hl
  ex de,hl
  ; de = string address
  xor a
  ; a = 0 (=new string)
  call STK_STORE
  rst 0x28 ; calculator
  db 0x17 ; concatenate to the empty string in the DEF FN expression
  db 0x38 ; end
  ret

fn_peekcs_size equ $-fn_peekcs

  endp

; vim: textwidth=64

; fn_peeko.z80s 
;
; PEEKO BASIC function for ZX Spectrum

; Version B-00-20150225

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a PEEKO function to Sinclair BASIC, that fetchs
; an 8-bit value from a memory address and an offset.
;
; Note: this function was written just to confirm that it's
; slower than its plain BASIC equivalent:
;
;   LET n=PEEK (a+o)

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this
; way (the actual names are unimportant):
;   10 DEF FN f(a,o)=USR peeko
; Where:
;   a     = base address to peek
;   o     = offset
;   peeko = address of the machine code routine

; Example:
;   PRINT FN f(1887,12)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN f(a,o) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     f
; -01     (
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     ,
; +08     o
; +09     14
; +10     0
; +11     [sign byte: 0=positive; 255=negative]
; +12     [16-bit number, LSB first]
; +14     0
; +15     )=

; --------------------------------------------------------------
; History of this file

; 2015-02-25: Written. Useless: this method is slower than 'LET
; n=PEEK (a+o)'.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_peeko,fn_peeko_size
fn_peeko:

  local DEFADD
  DEFADD equ 23563 ; system variable

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = base address
  ld e,(ix+12)
  ld d,(ix+13)
  ; de = offset
  add hl,de
  ld c,(hl)
  ld b,0
  ; bc = value, returned by USR
  ret

fn_peeko_size equ $-fn_peeko

  endp

; vim: textwidth=64

; fn_peeks.z80s 
;
; PEEK$ BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a PEEK$ function to Sinclair BASIC. PEEK$ gets
; a string from memory.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this way
; (the actual names are unimportant):
;   10 DEF FN p$(a,l)="" AND USR peeks
; Where:
;   a     = address of the first char of the string
;   l     = length of the string
;   peeks = address of the machine code routine

; Example:
;   PRINT FN p$(filename,10)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN p$(a,l) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     p$(
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     ,
; +08     l
; +09     14
; +10     0
; +11     [sign byte: 0=positive; 255=negative]
; +12     [16-bit number, LSB first]
; +14     0
; +15     )=

; --------------------------------------------------------------
; History of this file

; 2015-01-22: Written.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_peeks,fn_peeks_size
fn_peeks:

; ..............................
; System variables

  local DEFFADD
DEFADD equ 23563

; ..............................
; ROM routines

  local STK_STORE
STK_STORE   equ 0x2ab6
            ; Input:
            ;   A = flag
            ;   DE = string address
            ;   BC = string length

; ..............................
; Start

  ld ix,(DEFADD)
  ld e,(ix+4)
  ld d,(ix+5)
  ; de = string address
  ld c,(ix+12)
  ld b,(ix+13)
  ; bc = string length
  xor a
  ; a = 0 (=new string)
  call STK_STORE
  rst 0x28 ; calculator
  db 0x17 ; concatenate to the empty string in the DEF FN expression
  db 0x38 ; end
  ret

fn_peeks_size equ $-fn_peeks

  endp

; vim: textwidth=64

; fn_pokeo.z80s 
;
; POKEO BASIC function for ZX Spectrum

; Version B-00-20150122

; --------------------------------------------------------------
; Author

; Copyright (C) 2015 Marcos Cruz (programandala.net)

; --------------------------------------------------------------
; Description

; This code adds a POKEO function to Sinclair BASIC, that stores
; an 8-bit value into a memory address calculated from a base
; address and an offset.
;
; Note: this function was written just to confirm that it's
; slower than its plain BASIC equivalent:
;   POKE a+o,n

; --------------------------------------------------------------
; Usage

; A function must be defined this way
; (the actual names are unimportant):
;   10 DEF FN f(a,o,n)=USR pokeo
; Where:
;   a     = base address to poke into
;   o     = offset from the base address
;   n     = 8-bit number to poke
;   pokeo = address of the machine code routine

; Example:
;   RANDOMIZE FN f(16384,12,15)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN f(a,o,n) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     f(
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     ,
; +08     o
; +09     14
; +10     0
; +11     [sign byte: 0=positive; 255=negative]
; +12     [16-bit number, LSB first]
; +14     0
; +15     ,
; +16     n
; +17     14
; +18     0
; +19     [sign byte: 0=positive; 255=negative]
; +20     [16-bit number, LSB first]
; +22     0
; +23     )=

; --------------------------------------------------------------
; History

; 2015-02-25: Written. Useless: this method is slower than 'POKE
; a+o,n'.
;
; 2015-02-27: Improved comment.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_pokeo,fn_pokeo_size
fn_pokeo:

  local DEFADD
DEFADD equ 23563 ; system variable

  ld ix,(DEFADD)
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = base address
  ld e,(ix+12)
  ld d,(ix+13)
  ; de = offset
  add hl,de
  ; hl = address
  ld a,(ix+20)
  ld (hl),a
  ret

fn_pokeo_size equ $-fn_pokeo

  endp

; vim: textwidth=64

; fn_pokes.z80s 
;
; POKE$ BASIC command for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a POKE$ command to Sinclair BASIC.  POKE$ 
; pokes a string into a memory address.

; --------------------------------------------------------------
; Usage

; A BASIC a function must defined this way
; (the actual names are unimportant):
;   10 DEF FN p$(a,s$)=USR pokes
; Where:
;   a         = address to poke into
;   s$        = string to poke
;   pokes     = address of the machine code routine

; Example:
;   RANDOMIZE FN p$(16384,"Saluton!")

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN p$(a,s$) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     p$(
; +00     a
; +01     14
; +02     0
; +03     [sign byte: 0=positive; 255=negative]
; +04     [16-bit number, LSB first]
; +06     0
; +07     ,
; +08     s$
; +10     14
; +11     [string type]      
; +12     [string address, LSB first]
; +14     [string length, LSB first]
; +16     )=

; --------------------------------------------------------------
; History of this file

; 2014-08-08: Written.
;
; 2014-08-10: New: 'proc', 'local' and 'public', in order to
; make it possible to combine several modules into a single
; file.
;
; 2015-01-22: Some typos fixed. Renamed from "fn_poke.z80s" to
; "fn_pokes.z80s". Some changes for publication. Version B-00.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_pokes,fn_pokes_size
fn_pokes:

; ..............................
; System variables

  local DEFADD
DEFADD equ 23563

; ..............................

  ld ix,(DEFADD)
  ld c,(ix+14)
  ld b,(ix+15)
  ; bc = string length
  ld a,b
  or c
  ; z = empty string?
  ret z
  ld l,(ix+12)
  ld h,(ix+13)
  ; hl = string address
  ld e,(ix+4)
  ld d,(ix+5)
  ; de = destination address
  ldir
  ret

fn_pokes_size equ $-fn_pokes

  endp

; vim: textwidth=64

; fn_string.z80s 
;
; STRING$ BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Authors and license

; 1987-12:
; The original code was published in Microhobby Especial, issue 7,
; page 56, by unknown author:
;   http://microhobby.org/
;   http://microhobby.speccy.cz/mhf/MHEs7/mhes7_56.jpg

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)
;   Typed, translated and commented the code,
;   and finally adapted if to the Pasmo assembler.

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a STRING$ function to Sinclair BASIC.  STRING$
; produces a specified number of repeats of a string expression.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this
; way (the actual names are unimportant):
;   10 DEF FN s$(s$,n)="" AND USR string
; Where:
;   s$     = string expression to be repeated
;   n      = repetitions
;   string = address of the machine code routine

; Example:
;   PRINT FN s$("Saluton! ",3)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN s$(s$,n) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     s$(
; +00     s$
; +02     14
; +03     [string type]      
; +04     [string address, LSB first]
; +06     [string length, LSB first]
; +08     ,
; +09     n
; +10     14
; +11     0
; +12     [sign byte: 0=positive; 255=negative]
; +13     [16-bit number, LSB first]
; +15     0
; +16     )=

; --------------------------------------------------------------
; History of this file

; 2014-08-07: Written.
;
; 2014-08-10: New: 'proc', 'local' and 'public', in order to
; make it possible to combine several modules into a single
; file.
;
; 2014-12-14: Internal description finished.
;
; 2015-01-22: Some changes for publication. Version B-00.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_string,fn_string_size
fn_string:

  local copy
  local exit

; ..............................
; System variables

  local DEFFADD
DEFADD equ 23563

; ..............................
; ROM routines

  local STK_STORE
STK_STORE   equ 0x2ab6
            ; Input:
            ;   A = flag
            ;   DE = string address
            ;   BC = string length

  local STK_FETCH
STK_FETCH   equ 0x2bf1
            ; Output:
            ;   A = flag
            ;   DE = string address
            ;   BC = string length

  local HLHLDE
HLHLDE      equ 0x30a9 ; HL=HL*DE

; ..............................
; Start

  ld ix,(DEFADD)
  ld e,(ix+6)
  ld d,(ix+7)
  ; de = string length
  push de
  ld l,(ix+13)
  ld h,(ix+14)
  ; hl = number
  push hl
  call HLHLDE
  ld c,l
  ld b,h
  xor a
  ; a = 0 (=new string)
  ; de = string address, undetermined
  ; bc = length of the new string
  call STK_STORE
  rst 0x28 ; calculator
  db 0x17 ; concatenate to the empty string in the DEF FN expression
  db 0x31 ; duplicate
  db 0x38 ; end
  call STK_FETCH
  ; de = result string address
  ; bc = result string length
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = parameter string address
  pop ix ; parameter number
  pop bc ; parameter string length

; ..............................
copy:

  ; de = result string address (updated by ldir after every copy)
  ; hl = parameter string address
  ; bc = parameter string length
  ; ix = counter of copies still to be done

  ld a,ixh
  or ixl
  ; z = ixl=0?
  jr z,exit
  dec ix

  push hl
  push bc
  ldir ; copy the parameter string to the current position of the result string
  pop bc
  pop hl

  jr copy

; ..............................
exit:

  ld bc,1
  ret

fn_string_size equ $-fn_string

  endp

; vim: textwidth=64

; fn_termn.z80s
;
; A database BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a function to Sinclair BASIC that returns the
; n-th term (1 is the first one) in a given list of terms.

; --------------------------------------------------------------
; Usage

; A BASIC function must be defined this way:
; (the actual names are unimportant):
;   10 DEF FN t$(h$,n)="" AND USR termn
; Where:
;   h$    = the haystack, the searched string,
;           with the following structure:
;           every term is prefixed by byte 0, and followed
;           by byte 14 and an id byte.
;           Example of how a single term could be built:
;           let t$=chr$ 0+"term1"+chr$14+chr$ id1
;   n     = the ordinal number of the term that will be returned
;   termn = address of the machine code routine

; Example for a text adventure, where n$ holds a list of noun terms:
;   PRINT FN t$(n$,3)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN i(h$,n) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     t$(
; +00     h$
; +02     14
; +03     [string type]      
; +04     [string address, LSB first]
; +06     [string length, LSB first]
; +08     ,
; +09     n
; +10     14
; +11     0
; +12     [sign byte: 0=positive; 255=negative]
; +13     [16-bit number, LSB first]
; +15     0
; +16     )=

; --------------------------------------------------------------
; History of this file

; 2014-08-08: Started, with the code of <fn_term.z80s>.
;
; 2014-08-10: New: 'proc', 'public' and 'local', in order to
; make it possible to combine several modules into a single
; file.
;
; 2015-01-22: Some typos fixed. Improved description and usage.
; Some changes for publication. Version B-00.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_termn,fn_termn_size
fn_termn:

  local exit
  local haystack_char
  local needle_char
  local needle_end
  local needle_found
  local next_haystack_char

; ..............................
; System variables

  local DEFADD
DEFADD equ 23563

; ..............................
; ROM routines

  local STK_STO_$
STK_STO_$ equ 0x2ab2
  local STK_STORE
STK_STORE equ 0x2ab6
          ; Input:
          ;   A = flag
          ;   DE = string address
          ;   BC = string length

; ..............................
; Start

  ; hl' must be preserved!
  ; The only mention I've found about this issue is in
  ; Ian Logan's book "Understanding You Spectrum", 1982, page 43.
  exx
  push hl

; ..............................
; Parameters

  ld ix,(DEFADD)

  ld bc,0 ; term counter
  ld l,(ix+13)
  ld h,(ix+14)
  ; hl = n
  ld a,h
  or l
  ; z = is n zero?
  jr z,exit
  exx
  ld c,(ix+6)
  ld b,(ix+7)
  ; bc = len of h$
  ld a,b
  or c
  ; z = is h$ empty?
  jr z,exit
  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = start of h$

; ..............................
haystack_char:

  ; Check the current char of h$.
  ; hl = address of the current char in h$
  ld a,(hl)
  and a
  ; z = zero char found?
  jr z,needle_found

next_haystack_char:

  ; hl = address of the current char in h$
  ; bc = remaining length of h$
  inc hl
  dec bc
  ld a,b
  or c
  ; z = is the haystack exhausted?
  jr z,exit
  jr haystack_char

; ..............................
needle_found:

  ; A zero byte has been found in h$;
  ; it's the marker of a new term.
  ; But is it the term searched for?

  exx
  ; hl = n
  ; bc = term count
  inc bc
  ld d,h
  ld e,l
  ; cy = 0 (because of the 'and a' above)
  sbc hl,bc ; is this term the n term?
  ld h,d
  ld l,e
  exx
  ; z = is this term the n term?
  jr nz,next_haystack_char

; ..............................

  ; The desired n term has been found.
  ; Now its length has to be calculated.

  ld bc,0 ; length of the term
needle_char:
  ; hl = address of the current char in h$
  inc hl
  ld a,(hl)
  cp 14 ; is it the end of the needle string?
  ; z = end of string found?
  jr z,needle_end
  inc bc
  jr needle_char

; ..............................
needle_end:

  ; The end of the n term has been found.  In order to return it
  ; to BASIC, it has to be pushed onto the calculator stack, and
  ; added to the empty string already there.

  ; hl = address after the last char of the needle
  ; bc = length of the n term
  sbc hl,bc
  ld d,h
  ld e,l
  xor a
  ; a = 0 (=new string)
  ; de = address of the first char of the needle
  ; bc = length of the needle
  call STK_STO_$
  rst 0x28 ; calculator
  db 0x17 ; concatenate it to the empty string in the DEF FN expression
  db 0x38 ; end
  ld bc,1 ; to be ANDed with the string

exit:

  ; bc = 1 (valid string returned) or 0 (empty string returned)
  ; hl' must be restored
  pop hl
  push bc
  exx
  pop bc
  ret

fn_termn_size equ $-fn_termn

  endp

; vim: textwidth=64

; fn_term.z80s
;
; A database BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)
;
; Based on code written by Ricardo Serrial Wigge, published in:
;   Microhobby magazine, issue 77 (1986-05-06), pages 22-24:
;   http://microhobby.org/
;   http://microhobby.speccy.cz/mhf/077/MH077_22.jpg
;   http://microhobby.speccy.cz/mhf/077/MH077_23.jpg
;   http://microhobby.speccy.cz/mhf/077/MH077_24.jpg

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a function to Sinclair BASIC that
; returns the identifier of a given term in a list of terms.

; --------------------------------------------------------------
; Usage:
;
; A BASIC function must be defined this way
; (the actual names are unimportant):
;   10 DEF FN t(h$,n$)=USR term
; Where:
;   h$    = the haystack, the searched string,
;           with the following structure:
;           every term is prefixed by byte 0, and followed
;           by byte 14 and an id byte.
;           Example of how a single term could be built:
;           let t$=chr$ 0+"term1"+chr$14+chr$ id1
;   n$    = the needle, the string to be searched for
;           (it must include the starting byte 0 and the
;           ending byte 14).
;   term  = address of the machine code routine

; Example for a text adventure, where v$ holds a list of verb terms:
;   PRINT FN t(v$,CHR$ 0+"examine"+CHR$ 14)

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN i(h$,n$) is the following:

; offset  content
; ------  -------
; -03     DEF FN
; -02     t(
; +00     h$
; +02     14
; +03     [string type]      
; +04     [string address, LSB first]
; +06     [string length, LSB first]
; +08     ,
; +09     n$
; +11     14
; +12     [string type]      
; +13     [string address, LSB first]
; +15     [string length, LSB first]
; +17     )=

; --------------------------------------------------------------
; History of this file 

; 2014-08-07: Started, with the code of <fn_instr1.z80s>.
;
; 2014-08-10: Removed unnecessary saving and restoring of
; registers.  New: 'proc', 'local' and 'public', in order to
; make it possible to combine several modules into a single
; file.
;
; 2014-08-11: A bug was introduced by trying to reduce the
; number of jumps.
;
; 2014-08-12: The bug was fixed by comparing with the previous
; version A-00-201408071702.
;
; 2014-08-13: Simpler 'not_found' exit point, no jump.
;
; 2015-01-22: Some typos fixed. Improved description and usage.
; Some changes for publication. Version B-00.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_term,fn_term_size
fn_term:

  local again
  local found
  local haystack_exhausted
  local match
  local not_found
  local needle_exhausted

; ..............................
; System variables

  local DEFADD
DEFADD equ 23563

; ..............................
; Parameters

  ld ix,(DEFADD)
  ld a,(ix+6)
  or (ix+7) ; empty string?
  jr z, not_found

  ld e,(ix+4)
  ld d,(ix+5)
  ; de = address of h$
  ld l,(ix+6)
  ld h,(ix+7)
  ; hl = length of h$
  add hl,de
  ; hl = address after the end of h$

  ; Use ix+6 as temporary storage
  ld (ix+6),l
  ld (ix+7),h

  ld l,e
  ld h,d
  ; hl = address of h$

; ..............................
again:

  ; Start searching h$ for n$ from the current position of h$

  ld e,(ix+13)
  ld d,(ix+14)
  dec de
  ; de = address of n$ -1
  ld c,(ix+15)
  ld b,(ix+16)
  ; bc =length of n$

; ..............................
match:

  ; Keep on searching h$

  inc de
  ; de = address of current char of n$
  ; hl = address of current char of h$
  ld a,(de)
  cpi ; compare a with (hl) and increment hl
  ex af,af'
  push hl
  push de

  ld e,(ix+6)
  ld d,(ix+7)
  ; de = address after the end of h$
  and a
  sbc hl,de
  ; z = last char of h$?
  pop de
  pop hl
  jr z,haystack_exhausted

  ld a,b
  or c
  ; z = last char of n$?
  jr z,needle_exhausted

  ex af,af'
  ; z = (hl)=(de)?
  jr z,match
  jr again

; ..............................
needle_exhausted:

  ex af,af'
  ; z = (hl)=(de)?
  jr nz,again
  jr found

; ..............................
haystack_exhausted:

  ld a,b
  or c
  ; z = bc=0?
  jr nz,not_found

  ex af,af'
  ; z = (hl)=(de)?
  jr z,found

; ..............................
not_found:

  ld bc,0
  ret

; ..............................
found:

  ; hl = address after the last char of n$ in h$
  ld b,0
  ld c,(hl) ; take the term id
  ret

fn_term_size equ $-fn_term

  endp

; vim: textwidth=64

; fn_trunc.z80s
;
; TRUNC$ BASIC function for ZX Spectrum

; Version B-00-20150122

; This file is part of DEFFNder:
; http://programandala.net/en.program.deffnder.html

; --------------------------------------------------------------
; Author and license

; Copyright (C) 2014,2015 Marcos Cruz (programandala.net)

; You may do whatever you want with this work, so long as you
; retain the copyright notice(s) and this license in all
; redistributed copies and derived works. There is no warranty.

; --------------------------------------------------------------
; Description

; This code adds a TRUNC$ function to Sinclair BASIC, to strip
; the trailing spaces of a string.

; --------------------------------------------------------------
; Usage

; The routine is called from BASIC using a function defined this way:
; (the actual names are unimportant):
;   10 DEF FN t$(s$)="" AND USR trunc
; Where:
;   s$    = string to trunc
;   trunc = address of the machine code routine

; Example:
;   PRINT FN t$("En vilagxo de La Mancxo         ")

; --------------------------------------------------------------
; Internal

; During the execution of a FN, the system variable DEFADD holds
; the address of the first parameter of its DEF FN definition
; (the first address after the opening paren). The structure of
; DEF FN t$(s$) is the following:

; offset  content
; ------  -------
; -04     DEF FN
; -03     t$(
; +00     s$
; +02     14
; +03     [string type]
; +04     [string address, LSB first]
; +06     [string lenght, LSB first]
; +08     )=

; --------------------------------------------------------------
; History of this file

; 2014-08-06: Start.
;
; 2014-08-07: Finished. 
;
; 2014-08-10: Removed unnecessary saving and restoring of
; registers.  New: 'proc', 'public' and 'local', in order to
; make it possible to combine several modules into a single
; file.
;
; 2015-01-22: Some changes for publication. Version B-00.
;
; 2015-02-27: Improved comment.
;
; 2015-08-10: Revision. License.

; --------------------------------------------------------------

  proc

  ; The code is relocatable

  public fn_trunc,fn_trunc_size
fn_trunc:

  local exit
  local truncate
  local truncated

; ..............................
; System variables

  local DEFADD
DEFADD equ 23563

; ..............................
; ROM routines

  local STK_STORE
STK_STORE   equ 0x2ab6
            ; Input:
            ;   A = flag
            ;   DE = string address
            ;   BC = string lenght

; ..............................
; Parameters

  ld ix,(DEFADD)
  ld c,(ix+6)
  ld b,(ix+7)
  ; bc = string lenght
  ld a,b
  or c
  ; z = empty string?
  jr z, exit

  ld l,(ix+4)
  ld h,(ix+5)
  ; hl = string address
  add hl,bc
  ; hl = address after the last char of the string

; ..............................
truncate:

  ; bc = counter, remaining chars
  dec hl
  ; hl = address of the current char
  ld a,(hl)
  cp " "
  ; z=space?
  jr nz,truncated
  dec bc
  ld a,b
  or c
  ; z=end of the string?
  jr nz,truncate

; ..............................
truncated:

  ; bc = string lenght without the trailing spaces
  ld e,(ix+4)
  ld d,(ix+5)
  ; de = string address
  xor a  ; a = 0 (=new string)
  call STK_STORE
  rst 0x28 ; calculator
  db 0x17 ; concatenate to the empty string in the DEF FN expression
  db 0x38 ; end

; ..............................
exit:

  ld bc,1
  ret

fn_trunc_size equ $-fn_trunc

  endp

; vim: textwidth=64

Download

Also: DEFFNder in Github.

External related links