
  DEF FN
  compiled by Andrew Owen
  formatting, statistics & alternative versions by Jim Grimwood

  * History

The DEF FN (DEFine FuNction) command can be used to add a total of 52
new functions to Sinclair BASIC; 26 numeric functions (A-Z) and 26
string functions (A$-Z$).

Owing to the internal structure of Sinclair BASIC, the best place to
store these functions is in line 0. This prevents the line being
accidentally edited or deleted and also gives the maximum possible speed
(on a standard Spectrum with no Interface I attached, line 1 can be
converted into line 0 with POKE 23756,0).

The more complicated or frequently called functions should be given the
higher priority; for example, the MOD function should normally occur first.

Where possible, the following functions are compatible or partially
compatible with their Beta BASIC equivalents. Some of the numeric
functions are restricted to dealing with 8-bit numbers (0-255) while
their Beta BASIC equivalents can handle 16-bit numbers. This is because
dealing with 16-bit numbers in BASIC would be tortuously slow. Recursive
functions can be constructed, but are avoided where possible (in order
to permit compilation with HiSoft BASIC).

1$  = non-null 1 character string
2$  = 2 character string
d8  = 8-digit decimal integer (0-99999999)
h4$ = 4 digit hex string ("0000"-"FFFF")
i8  = 8-bit positive integer (0-255)
i16 = 16-bit positive integer (0-65535)

A ROM bug, mentioned on http://www.nonowt.com/magfold/articfol/bugs_in.html

states:

The FN error (from ZX Computing Monthly - Toni Baker)

When a user-defined function is evaluated the value of the system
variable (CH_ADD) which stores the address of the next character to be
interpreted, is saved on the machine stack instead of in one of the
dynamic variables such as (X_PTR) during the evaluation of the function.
This means that any user-defined function which causes the BASIC program
area to move up or down in memory will cause error report C; Nonsense in
BASIC. This is not normally possible, but exceptions can occur if the
DEF FN statement contains the function USR, and could also conceivably
happen with the function INKEY#X. To avoid this bug you must ensure that
any machine code subroutine which is called by a DEF FN statement does
not disturb the BASIC program area (e.g. it is impossible to define a
function FN D (X,Y) which would delete BASIC lines X to Y inclusive
without also manipulating the machine stack).

Keep this remark in mind especialy with DEF FN x()=USR adress or with
creating a channel.

The characters used here are used as example but in some cases DEF FN
can depend on other DEF FN when they are nested. This makes them more
readable and in some cases faster. So if you copy them have a good look.

The EVEN(number, 0 or 1) can save a lot of space since it checks the
last BIT of a number so:

the last bit-check of the AND FN logically will hold as the last part

(1 AND INT FN V(FN V(FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8),4),2)
      +INT FN V(FN V(FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8),4),2)=2)

which is replaced with

(1 AND FN e(x,1) + FN e(y,1)=2)

since it is in fact a Bit_0 check which is an ODD or EVEN check. This
saves calculating time as well as space.

    Contents

                          length  mem  time (sec)   recursive alternative
  *  1  AND (i8,i8)     A    940   84  1.94                238  837  1.88
  *  2  BIN (a$)        B     29   36  0.000005
  *  3  BIN$ (i8)       B$   405  129  0.84                 66  629  0.32
  *  4  CHAR$ (i16)     C$    58   20  0.02
  *  5  DEC (h4$)       D    240   20  0.04->0.06          108  226  0.58
  *  6  DEG (n)         G     25   14  0.000005
  *  7  DPEEK (a)       P     37   20  0.000005
  *  8  EVEN/ODD (a,b)  E     78   22  0.02
  *  9  FREE ()         F     28,92    0.000005,0.02
  * 10  HEX$ (i16)      H$   401   69  0.44                125  507  0.48
  * 11  ITEM ()         T    124  bug  0.22                154 7195varies
  * 12  LEFT$ (a$,n)    L$    28   12  0.000005
  * 13  LOWER$ (1$)     W$    59 1byte 0.000005            111 any$  0.02
  * 14  MAX (n,o)       M     40   16  0.02
  * 15  MAX$ (a$,b$)    A$    44   24  0.000005
  * 16  MID$ (a$,n,o)   M$    47   10  0.000005
  * 17  MIN (n,o)       Q     40   10  0.02
  * 18  MIN$ (a$,b$)    Q$    44   20  0.000005
  * 19  MOD (n,o)       V     30   16  0.000002
  * 20  NOT/CPL (i8)    N    647   90  1.1                 106  584  1.24
  * 21  NUMBER (2$)     U     48   16  0.000002
  * 22  OR (i8,i8)      O   1110   88  2.66                238  837  1.88
  * 23  RAD (n)         R     25   16  0.000002
  * 24  RNDM (n)        I     28   10  0.02
  * 25  RIGHT$ (a$,n)   R$    43   10  0.000005
  * 26  STRING$ (a$,n)  S$    77  482  0.1
  * 27  TIME ()         Z    130   26  0.12
  * 28  TIME$ (n)       T$   375  113  0.56                175 1036  0.82
  * 29  UPPER$ (1$)     U$    60 1byte 0.000002            111 any$  0.02
  * 30  XOR (i8,i8)     X   1030   82  2.64                238  837  1.88


RELATED FUNCTIONS

AND (i8,i8), NOT (i8), OR (i8,i8), XOR (i8,i8)
BIN (a$), BIN$ (i8), DEC (h4$), HEX$ (i16)
CHAR$ (i16), NUMBER (2$)
DEG (n), RAD (n)
DPEEK (a), FREE (), ITEM ()
EVEN (a,b), ODD (a,b), MOD (n,o), RNDM (n)
LEFT$ (a$,n), MID$ (a$,n,o), RIGHT$ (a$,n), STRING$ (a$,n)
LOWER$ (1$), UPPER$ (1$)
MAX (n,o), MAX$ (a$,b$), MIN (n,o), MIN$ (a$,b$)
TIME (), TIME$ (n)


     AND (i8,i8): depends on FN v() and FN e(a,0 or 1)

   1 DEF FN A(x,y)=(128 AND INT (x/128)+INT (y/128)=2)
                  +( 64 AND INT (FN V(x,128)/64)+INT (FN V(y,128)/64)=2)
                  +( 32 AND INT (FN V(FN V(x,128),64)/32)+INT (FN V(FN V(y,128),64)/32)=2)
                  +( 16 AND INT (FN V(FN V(FN V(x,128),64),32)/16)+INT (FN V(FN V(FN V(y,128),64),32)/16)=2)
                  +(  8 AND INT (FN V(FN V(FN V(FN V(x,128),64),32),16)/8)+INT (FN V(FN V(FN V(FN V(y,128),64),32),16)/8)=2)
                  +(  4 AND INT (FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8)/4)+INT (FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8)/4)=2)
                  +(  2 AND INT (FN V(FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8),4)/2)+INT (FN V(FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8),4)/2)=2)
                  +(  1 AND FN e(x,1)+FN e(y,1)=2)

3010 REM *LOAX: a=d8; b=d8; c: 0=OR, 1=AND, 2=XOR; d=number of bits
3011 REM max.bits = 27 (99999999 = %101111101011110000011111111)
3012 DEF FN l(a,b,c,d)
     =(2^(INT (c/10)) AND (a-INT (a/2)*2)+(b-INT (b/2)*2)
               AND (a-INT (a/2)*2)+(b-INT (b/2)*2)<>c-INT (c/10)*10)
     +VAL (("FN l(INT (a/2),INT (b/2),c+10,d)+" AND c<d*10)+("0"))


     BIN (a$)

   2 DEF FN B(b$)=VAL(CHR$ 196+b$)


     BIN$ (i8) : depends on FN v() and FN e(a,0 or 1)

   3 DEF FN B$(i8)=STR$ INT (b/128)
                  +STR$ INT (FN V(b,128)/64)
                  +STR$ INT (FN V(FN V(b,128),64)/32)
                  +STR$ INT (FN V(FN V(FN V(b,128),64),32)/16)
                  +STR$ INT (FN V(FN V(FN V(FN V(b,128),64),32),16)/8)
                  +STR$ INT (FN V(FN V(FN V(FN V(FN V(b,128),64),32),16),8)/4)
                  +STR$ INT (FN V(FN V(FN V(FN V(FN V(FN V(b,128),64),32),16),8),4)/2)
                  +STR$ FN e(b,1)

3030 REM DEC2BIN: a=i8 (255 = %11111111)
3031 DEF FN b(a)=(a-INT (a/2)*2)
                +VAL (("10*FN b(INT (a/2))+" AND a)+("0"))


     CHAR$ (i16)

   4 DEF FN C$(n)=CHR$ INT (n/256)+CHR$ (n-INT (n/256)*256)


     DEC (h4$)

   5 DEF FN D(h$)=(CODE h$(1)-48-7*(h$(1)>"9"))*4096
                 +(CODE h$(2)-48-7*(h$(2)>"9"))*256
                 +(CODE h$(3)-48-7*(h$(3)>"9"))*16
                 + CODE h$(4)-48-7*(h$(4)>"9")

3050 REM *HEX2DEC: h$=h7$ ($5F5E0FF = 99999999)
3051 DEF FN d(h$)=16^(LEN h$-1)*(CODE h$-48-(7 AND CODE h$>64))
                +VAL (("FN d(h$(2 TO))+" AND LEN h$>1)+"0")


     DEG (n)

   6 DEF FN G(n)=n/PI*180


     DPEEK (a)

   7 DEF FN P(a)=PEEK a+256*PEEK (a+1)

The companion DPOKE command can be implemented as follows:

POKE address,number-INT(number/256)*256:POKE address+1,INT(number/256)


    **EVEN**(n,/**0**/ or 1) ( or **ODD**(n, 0 or /**1**/))

   8 DEF FN e(a,b)=((a/2 = INT(a/2)) AND NOT b)+((a/2<>INT(a/2)) AND b)


     FREE ()

   9 DEF FN F()=65536-USR 7962

or if you're using a custom ROM which doesn't have the free-mem routine:

     DEF FN F()=(PEEK 23731*256)+PEEK 23730-((PEEK 23654*256)+PEEK 23653)-110


     HEX$ (i16): depends on FN v()

  10 DEF FN H$(n)=CHR$ (INT (n/4096)+7*(INT (n/4096)>9)+48)
                 +CHR$ (INT (FN V(n,4096)/256)+7*(INT (FN V(n,4096)/256)>9)+48)
                 +CHR$ (INT (FN V(FN V(n,4096),256)/16)+7*(INT (FN V(FN V(n,4096),256)/16)>9)+48)
                 +CHR$ ((FN V(FN V(FN V(n,4096),256),16))+7*(INT FN V(FN V(FN V(n,4096),256),16)>9)+48)

3100 REM *DEC2HEX: a=d8
3101 DEF FN h$(a)=VAL$ (("FN h$("+STR$ INT (a/16)+")+"""
                        +CHR$ (FN v(a,16)+48+(7 AND FN v(a,16)>9))
                        +"""" AND a)
                       +("""""" AND NOT a))


     ITEM ()

  11 DEF FN T()=(1 AND PEEK (1+FN P(23639))=34)
               +(2 AND PEEK (FN P(23639))>13 AND PEEK (1+FN P(23639))>34)

3110 REM ITEM:
3111 DEF FN t(a,b,c$)
           =VAL (("FN t(a+1+(5 AND b=14),
                   PEEK a,
                   (c$ AND NOT VAL c$)+(e$ AND VAL c$))"
                  AND (c$=d$ OR NOT VAL c$) AND b<>13)
                 +("a" AND (c$=e$ AND VAL c$ OR b=13))
                )
3112 LET da=FN p(23639)+(PEEK FN p(23639)<>44): LET db=PEEK da:
     LET d$="b=228": LET e$="b<>228 AND b<>44 AND b<>32":
     LET a$=(d$ AND db<>44)+(e$ AND db=44): LET item=FN t(da,db,a$)


     LEFT$ (a$,n)

  12 DEF FN L$(s$,l)=S$( TO l)


     LOWER$ (1$)

  13 DEF FN W$(l$)=CHR$ ((CODE l$)+(32 AND CODE l$>64 AND CODE l$<92))

3130 REM LOWER: a$=any$
3131 DEF FN w$(a$)=CHR$ (CODE a$+(32 AND a$>="A" AND a$<"["))
                  +VAL$ (("FN w$("""+a$(2 TO)+""")" AND LEN a$>1)
                        +("""""" AND LEN a$=1))


     MAX (n,o)

  14 DEF FN M(x,y)=(x+y+ABS (x-y))/2


     MAX$ (a$,b$)

  15 DEF FN A$(x$,y$)=(x$ AND x$>y$)+(y$ AND y$>=x$)


     MID$ (a$,n,o)

  16 DEF FN M$(s$,s,l)=s$(s TO s-1+l)


     MIN (n,o)

  17 DEF FN Q(x,y)=(x-y+ABS (x+y))/2


     MIN$ (a$,b$)

  18 DEF FN Q$(x$,y$)=(x$ AND x$<y$)+(y$ AND y$<=x$)


     MOD (n,o)

  19 DEF FN V(x,y)=x-y*INT (x/y)


     NOT (i8): depends on FN v()

  20 DEF FN N(n)=(128 AND ABS (INT (n/128)-1))
                +( 64 AND ABS (INT (FN V(n,128)/64)-1))
                +( 32 AND ABS (INT (FN V(FN V(n,128),64)/32)-1))
                +( 16 AND ABS (INT (FN V(FN V(FN V(n,128),64),32)/16)-1))
                +(  8 AND ABS (INT (FN V(FN V(FN V(FN V(n,128),64),32),16)/8)-1))
                +(  4 AND ABS (INT (FN V(FN V(FN V(FN V(FN V(n,128),64),32),16),8)/4)-1))
                +(  2 AND ABS (INT (FN V(FN V(FN V(FN V(FN V(FN V(n,128),64),32),16),8),4)/2)-1))
                +(  1 AND ABS (FN V(FN V(FN V(FN V(FN V(FN V(FN V(n,128),64),32),16),8),4),2)-1))

3205 REM *NOT/CPL: a=d8; d=number of bits; e=0
3206 REM max.bits = 27 (99999999 = %101111101011110000011111111)
3207 DEF FN n(a,d,e)=2^e*(NOT a-INT (a/2)*2)
                +VAL (("FN n(INT (a/2),d,e+1)+" AND e<d-1)+("0"))


     NUMBER (2$)

  21 DEF FN U(c$)=256*CODE c$(1)+CODE c$(2)


     OR (i8,i8): depends on FN v()

  22 DEF FN O(x,y)=(128 AND INT ((x/128) OR INT (y/128)=1))
                  +( 64 AND INT ((FN V(x,128)/64) OR INT (FN V(y,128)/64)=1))
                  +( 32 AND INT ((FN V(FN V(x,128),64)/32) OR INT (FN V(FN V(y,128),64)/32)=1))
                  +( 16 AND INT ((FN V(FN V(FN V(x,128),64),32)/16) OR INT (FN V(FN V(FN V(y,128),64),32)/16)=1))
                  +(  8 AND INT ((FN V(FN V(FN V(FN V(x,128),64),32),16)/8) OR INT (FN V(FN V(FN V(FN V(y,128),64),32),16)/8)=1))
                  +(  4 AND INT ((FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8)/4) OR INT (FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8)/4)=1))
                  +(  2 AND INT ((FN V(FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8),4)/2) OR INT (FN V(FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8),4)/2)=1))
                  +   1 AND INT (FN V(FN V(FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8),4),2) OR INT FN V(FN V(FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8),4),2)=1)

3010 REM *LOAX: a=d8; b=d8; c: 0=OR, 1=AND, 2=XOR; d=number of bits
3011 REM max.bits = 27 (99999999 = %101111101011110000011111111)
3012 DEF FN l(a,b,c,d)
     =(2^(INT (c/10)) AND (a-INT (a/2)*2)+(b-INT (b/2)*2)
               AND (a-INT (a/2)*2)+(b-INT (b/2)*2)<>c-INT (c/10)*10)
     +VAL (("FN l(INT (a/2),INT (b/2),c+10,d)+" AND c<d*10)+("0"))


     RAD (n)

  23 DEF FN R(n)=n/180*PI


     RNDM (n)

  24 DEF FN I(n)=INT (RND*(n+1))


     RIGHT$ (a$,n)

  25 DEF FN R$(s$,l)=s$(LEN s$+1-l TO LEN s$)


     STRING$ (a$,n):recursive

  26 DEF FN S$(i$,i)=i$+VAL$ """""+FN s$(i-1,i$)"( TO 2+(12 AND i<>1))


     TIME ()

  27 DEF FN Z()=FN M(65536*PEEK 23674+256*PEEK 23673+PEEK 23672
                    ,65536*PEEK 23674+256*PEEK 23673+PEEK 23672)


     TIME$ (n)

  28 DEF FN T$(n)=STR$ INT (n/1800000)
                 +STR$ INT (FN V(n,1800000)/180000)+":"
                 +STR$ INT (FN V(FN V(n,1800000),180000)/30000)
                 +STR$ INT (FN V(FN V(FN V(n,1800000),180000),30000)/3000)+":"
                 +STR$ INT (FN V(FN V(FN V(FN V(n,1800000),180000),30000),3000)/500)
                 +STR$ INT (FN V(FN V(FN V(FN V(FN V(n,1800000),180000),30000),3000),500)/50)

3280 REM TIME$: n=anyn; t=1,800,000; d=10
3281 DEF FN t$(n,t,d)
     =STR$ INT (n/t)+(":" AND d=6 AND t>500)
     +VAL$ (("FN t$("
            +STR$ (n-INT (n/t)*t)+","
            +STR$ (t/d)+","
            +STR$ (d-4+(8 AND d=6))
            +")" AND t>=500)
           +("""""" AND t<500))


     UPPER$ (1$)

  29 DEF FN U$(u$)=CHR$ ((CODE u$)-(32 AND CODE u$>96 AND CODE u$<124))

3290 REM UPPER: a$=any$
3291 DEF FN u$(a$)=CHR$ (CODE a$-(32 AND a$>="a" AND a$<"{"))
                  +VAL$ (("FN u$("""+a$(2 TO)+""")" AND LEN a$>1)
                  +("""""" AND LEN a$=1))


     XOR (i8,i8): depends on FN v()

  30 DEF FN X(x,y)=(128 AND INT (x/128)<>INT (y/128))
                  +( 64 AND INT (FN V(x,128)/64)<>INT (FN V(y,128)/64))
                  +( 32 AND INT (FN V(FN V(x,128),64)/32)<>INT (FN V(FN V(y,128),64)/32))
                  +( 16 AND INT (FN V(FN V(FN V(x,128),64),32)/16)<>INT (FN V(FN V(FN V(y,128),64),32)/16))
                  +(  8 AND INT (FN V(FN V(FN V(FN V(x,128),64),32),16)/8)<>INT (FN V(FN V(FN V(FN V(y,128),64),32),16)/8))
                  +(  4 AND INT (FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8)/4)<>INT (FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8)/4))
                  +(  2 AND INT (FN V(FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8),4)/2)<>INT (FN V(FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8),4)/2))
                  +   1 AND INT FN V(FN V(FN V(FN V(FN V(FN V(FN V(x,128),64),32),16),8),4),2)<>INT FN V(FN V(FN V(FN V(FN V(FN V(FN V(y,128),64),32),16),8),4),2)

3010 REM *LOAX: a=d8; b=d8; c: 0=OR, 1=AND, 2=XOR; d=number of bits
3011 REM max.bits = 27 (99999999 = %101111101011110000011111111)
3012 DEF FN l(a,b,c,d)
     =(2^(INT (c/10)) AND (a-INT (a/2)*2)+(b-INT (b/2)*2)
               AND (a-INT (a/2)*2)+(b-INT (b/2)*2)<>c-INT (c/10)*10)
     +VAL (("FN l(INT (a/2),INT (b/2),c+10,d)+" AND c<d*10)+("0"))


This article began as a thread in the World of Spectrum Sinclair BASIC
forum.