Text Functions

; $$$$$$$$$$$$$$$$$$ MAGIC-ARM $$$$$$$$$$$$$$$$$$$
; *************** STAR^2 SOFTWARE ****************
; ?????????????????? TEXT.INC ????????????????????

; fast portable ARM text operations

; text.n t          ; get # characters (size-1)
; text.write a, b   ; copy with no 0 after
; text.copy a, b    ; standard copy with 0 after
; text.copy.n ...   ; copy with maximum size
; text.attach a, b  ; attach b to a; "concencate"
; text.attach.c...  ; attach character
; text.compare a, b ; compare. return <0>
; text.find t, c    ; search for c. return &/0
; text.find.last... ; search for c reverse
; text.count.c t, c ; count occurrances of c
; text.count.n t    ; count # lines
; text.begins a, b  ; begins with b?
; text.ends a, b    ; ends with b?
; text.upper t      ; convert to uppercase
; text.lower t      ; convert to lowercase
; text.reverse t    ; reverse
; text.expand t, n  ; shift right
; text.prefix a, b  ; prepend
; text.enclose.c... ; enclose in 'c'
; text.align t...   ; align to size: '007F'

; text.array.equal ta, t, n
; text.array.begins ta, t, n

; text.skip.while t, type
; text.skip.until t, type
; text.copy.while a, b, type
; text.copy.until a, b, type

; set.source r
; set.token r
; set.stream s, t

; skip.while type
; skip.until type
; copy.while type
; copy.until type

; skip.space
; skip.white
; skip.comment
; skip.all

; i2t n, t         ; number/text conversions
; u2t n, t
; h2t n, t
; b2t n, t

; t2i t
; t2u t
; t2h t
; t2b t

;;;;;;;;;;;;;;; CHARACTER TABLES ;;;;;;;;;;;;;;;;;

; ILT - insensitive lookup table. A-Z/a-z are
; the same. increases processing speed by
; many times. example: if (tt[a]=tt[b]) instead
; of: if ((a>='a'&a<='z')&(b>='a'&b<='z')) |
; ((a>='A'&a<='Z')&(b>='Z'&b<='Z'))

; TLT - type lookup table. each byte contains
; C.X BITs to determine its type fast in one
; comparison: if tt[c]&SYMBOL

macro define.xlt {

align 4

ILT db \
 00h,01h,02h,03h,04h,05h,06h,07h,\
 08h,09h,0Ah,0Bh,0Ch,0Dh,0Eh,0Fh,\
 10h,11h,12h,13h,14h,15h,16h,17h,\
 18h,19h,1Ah,1Bh,1Ch,1Dh,1Eh,1Fh,\
 20h,21h,22h,23h,24h,25h,26h,27h,\
 28h,29h,2Ah,2Bh,2Ch,2Dh,2Eh,2Fh,\
 30h,31h,32h,33h,34h,35h,36h,37h,\
 38h,39h,3Ah,3Bh,3Ch,3Dh,3Eh,3Fh,\
 40h,41h,42h,43h,44h,45h,46h,47h,\
 48h,49h,4Ah,4Bh,4Ch,4Dh,4Eh,4Fh,\
 50h,51h,52h,53h,54h,55h,56h,57h,\
 58h,59h,5Ah,5Bh,5Ch,5Dh,5Eh,5Fh,\
 60h,41h,42h,43h,44h,45h,46h,47h,\
 48h,49h,4Ah,4Bh,4Ch,4Dh,4Eh,4Fh,\
 50h,51h,52h,53h,54h,55h,56h,57h,\
 58h,59h,5Ah,7Bh,7Ch,7Dh,7Eh,7Fh

TLT db \
 00h,80h,80h,80h,80h,80h,80h,80h,\
 80h,80h,40h,80h,80h,40h,80h,80h,\
 80h,80h,80h,80h,80h,80h,80h,80h,\
 80h,80h,80h,80h,80h,80h,80h,80h,\
 20h,10h,04h,04h,10h,04h,04h,04h,\
 04h,04h,04h,04h,04h,04h,10h,04h,\
 01h,01h,01h,01h,01h,01h,01h,01h,\
 01h,01h,04h,04h,04h,04h,04h,10h,\
 10h,0Ah,0Ah,0Ah,0Ah,0Ah,0Ah,02h,\
 0Ah,02h,02h,0Ah,02h,0Ah,02h,02h,\
 02h,02h,02h,02h,02h,02h,02h,02h,\
 02h,02h,02h,04h,04h,04h,04h,10h,\
 04h,0Ah,0Ah,0Ah,0Ah,0Ah,0Ah,02h,\
 0Ah,02h,02h,0Ah,02h,0Ah,02h,02h,\
 02h,02h,02h,02h,02h,02h,02h,02h,\
 02h,02h,02h,04h,04h,04h,04h,80h
}

define.xlt

;            76543210b
C.NULL     = 00000000b ; 0
C.NUMBER   = 00000001b ; 0-9
C.ALPHA    = 00000010b ; A-Z, a-z
C.SYMBOL   = 00000100b ; all symbols except _.?!@$
C.NUMERIC  = 00001000b ; A-F/a-f, h,b,k,m/H,B,K,M
C.SYMBOLIC = 00010000b ; _.?!@$
C.SPACE    = 00100000b ; ' ', '/t'
C.RETURN   = 01000000b ; 0Dh, 0Ah
C.IGNORE   = 10000000b ; extended: 1.XXXXXXXb
C.KEYWORD  = 11111111b

C.DIGIT    = C.NUMBER or C.NUMERIC
C.NAME     = C.ALPHA or C.NUMBER or C.SYMBOLIC
C.SYMBOLS  = C.SYMBOL or C.SYMBOLIC
C.ALPHAN   = C.ALPHA or C.NUMBER
C.VISIBLE  = C.ALPHAN or C.SYMBOLS
C.WHITE    = C.SPACE or C.RETURN
C.BLANK    = C.WHITE or C.IGNORE
C.END      = C.SYMBOL or C.WHITE
C.0        = 0

; is c of type? a1=c, a2=type

function is.c, c, type
  . a1&0FFh, a3=TLT, a3+a1
  . (u8) a1=*a3, a1&a2
endf

macro is.c c, t {
  . a1=c, a2=t
  is.c
}

macro if.is c, t {
  is.c c, C.#t
  .if true
}

numeric ZERO.C='0', SPACE.C=' ',\
 COMMA.C=',', COLIN.C=':', SEMI.C=';',\
 POUND.C='#', ASSIGN.C='=', SLASHB.C='\'

NL equ ,0Dh,0Dh,

macro .if.text.equal a, b
 { !if text.equal, a, b }
macro .if.not.text.equal a, b
 { !if.n text.equal, a, b }
macro .if.text.find a, b
 { !if text.find, a, b }
macro .if.text.find.last a, b
 { !if text.find.last, a, b }
macro .if.text.begins a, b
 { !if text.begins, a, b }
macro .if.text.ends a, b
 { !if text.ends, a, b }

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; text.end(t) - advance a1 to end (*t=0)

function text.end, t
  alias t=a1, c=v1
  . c=1
  .while c           ; until 0
    . (u8) c=*t++    ; read c
  .endw
  . t--
endf

; text.n(t) - get text length, # characters

function text.n, t
  alias t=a1, b=a2, c=v1
  . c=1, b=t
  .while c           ; until 0
    . (u8) c=*t++    ; read c
  .endw
  . t--, t-b         ; return current-start
endf

; text.write(a, b) - copy with no 0 after

function text.write, a, b
  alias a=a1, b=a2, c=v1
  .forever
    . (u8) c=*b++    ; read
    .if c=0
      return
    .end
    . (u8) *a++=c    ; copy
  .endfv
endf

; text.copy(a, b) - standard copy with 0 after

function text.copy, a, b
  alias a=a1, b=a2, c=v1
  . c=1
  .while c
    . (u8) c=*b++    ; read
    . (u8) *a++=c    ; copy
  .endw
  . a--              ; return end
endf

; text.copy.n(a, b, n) - copy with maximum
; size specified

function text.copy.n, a, b, n
  alias a=a1, b=a2,\
   n=a3, c=v1
  .repeat n          ; # times
    . (u8) c=*b++    ; read
    . (u8) *a++=c    ; copy
    .if c=0          ; end?
      .break
    .end
  .endr
  . a--
endf

; text.attach(a, b) - attach b to a

function text.attach, a, b
  alias a=a1,\
   b=a2, p=v1
  . p=b
  text.end
  . b=p
  text.copy
endf

; text.attach.c(t, c) - attach c to t

function text.attach.c, t, c
  alias t=a1, c=v2
  . c=a2
  text.end
  . (u8) *t++=c, (u8) *t=0
endf

; text.compare(a, b) - lexical comparison.
; return <0>

function text.compare, a, b
  alias a=a1, b=a2,\
   c=v1, d=v2
   . c=1, d=c
  .while c=d         ; while equal
    . (u8) c=*a++
    . (u8) d=*b++
    .if c=0          ; and both nonzero
      .break
    .end
    .if d=0
      .break
    .end
  .endw
  . a=c-d            ; return *a-*b
endf

; text.equal(a, b) - equal? return 1/0

function text.equal, a, b
  text.compare
  .if false
    return 1
  .end
endf 0

; text.find(t, c) - search for character.
; return address or 0

function text.find, t, c
  alias t=a1,\
   c=v1, key=a2
  .forever           ; loop
    . (u8) c=*t      ; read c
    .if c=0          ; end?
      return 0       ; not found
    .end
    .if c=key        ; found?
      return t       ; t=address
    .end
    . t++            ; next
  .endfv
endf

; text.find.last(t, c) - search for c reverse

function text.find.last, t, c
  alias t=a1, c=a2,\
   key=v1, p=v2
  . key=c, p=t       ; save begin
  . c=1
  .while c           ; advance to end-1,
    . (u8) c=*t++    ; last character
  .endw
  . t-2
  .while t>p         ; loop backwards
    . (u8) c=*t      ; until beginning
    .if c=key        ; found
      return t       ; t=address
    .end
    . t--            ; previous
  .endw
endf 0               ; not found

; text.count.c(t, c) - count # of
; characters in text

function text.count.c, t, c
  alias p=v1, c=v2, n=v3
  . p=a1, c=a2, n=0
  .while true
    get p=text.find p, c
    .if true
      . p++, n++
    .end
  .endw
endf n

; text.count.n(t) - count # lines

function text.count.n, t
  text.count.c a1, 0Dh
  .if a1>0
    . a1++
  .end
endf

; text.begins(a, b) - a begins with b?

function text.begins, a, b
  alias a=a1, b=a2,\
   c=v1, d=v2, e=v3
  . c=1, d=c
  .while c=d         ; while equal
    . (u8) c=*a++    ; load *a/*b
    . (u8) d=*b++
    . e=c|d          ; and both nonzero
    .breakz          ; break if either=0
  .endw
  .if d<>0           ; *b must=0
    return 0
  .end
endf

; text.ends(a, b) - a ends with b?

function text.ends, a, b
  alias \
   a=v1, b=v2, p=v3
  . a=a1, b=a2
  text.end            ; a=end(a)-length(b)
  . p=a1, a1=b
  text.n
  . p-a1, a1=p, a2=b
  text.compare
  .if false
    return 1
  .end
endf 0

; text.upper(t) - convert to uppercase

function text.upper, t
  alias t=a1, c=v1
  .forever
    . (u8) c=*t      ; get c
    .if c=0          ; end?
      return t
    .end
    .if c>=97        ; lowercase?
      .if c<=122
        . c-32
        . (u8) *t=c  ; copy c
      .end
    .end
    . t++            ; next c
  .endfv
endf

; text.lower(t) - convert to lowercase

function text.lower, t
  alias t=a1, c=v1
  .forever
    . (u8) c=*t      ; get c
    .if c=0          ; end?
      return
    .end
    .if c>=65        ; uppercase?
      .if c<=90
        . c+32
        . (u8) *t=c  ; copy c
      .end
    .end
    . t++            ; next c
  .endfv
endf

; text.reverse(t) - reverse text

function text.reverse, t
  alias t=a1,\
   p=a2, s=a3,\
   c=v1, d=v2
  . s=t, c=1         ; save start
  .while c
    . (u8) c=*t++    ; advance to end
  .endw
  . p=t-2, t=s
  .while t<p         ; exchange *t++/*p--
    . (u8) c=*t
    . (u8) d=*p
    . (u8) *t++=d
    . (u8) *p=c
    . p--
  .endw
  . t--
endf

; expand; shift all characters right.
; example: 'abc123' becomes 'XXXabc123'
; after expand 3 where X is unknown

function text.expand, t, n
  alias t=v1, p=v2,\
   n=v3, tn=v4, a=v5, b=v6
  . t=a1, n=a2
  get tn=text.n t
  . a=t+tn, a--, b=a, a+n
  .repeat tn
    . (u8) *a=*b, a--, b--
  .endr
endf

; insert text at beginning

function text.prefix, a, b
  alias a=v1, b=v2, n=v3
  . a=a1, b=a2
  get n=text.n b
  text.expand a, n
  text.write a, b
endf

; enclose t in b/egin and e/nd characters.
; example: text.enclose t, 28h, 29h
; '(', ')'

function text.enclose.c, t, b, e
  alias t=v1, b=v2, e=v3,\
   p=v4, n=v5, c=v6
  . t=a1, b=a2, e=a3
  get n=text.n t
  text.expand t, 1
  . p=t, (u8) *p=b, p=t+n, p++
  . (u8) *p++=e, (u8) *p=0
endf

; another way:

; text.copy buffer, b
; text.attach buffer, a
; text.copy a, buffer

; prefix text with c's ('0', ' ', etc)
; or ensure maximum n. example:
; before: text t='7FAB'
; text.align t, 30h, 8
; after: t='00007FAB', aligned to hex32

function text.align, t, c, n
  alias t=v1, c=v2,\
   n=v4, p=v5, tn=v6
  . t=a1, c=a2, n=a3
  get tn=text.n t
  .if n=tn            ; same size
    return            ; do nothing
  .end
  .if tn>n            ; exceeds maximum
    . t+n, (u8) *t=0  ; end at t+n
    return
  .end
  . n-tn              ; expand t
  text.expand t, n
  .repeat n
    . (u8) *t++=c
  .endr
  . t+tn, (u8) *t=0   ; terminate
endf

; search text array ta for t using text.equal.
; return index or -1 (<0) if not found. ta is
; an array of text addresses (texts)

function text.array.equal, ta, t, n
  alias ta=v1, t=v2,\
   n=v3, i=v4, p=v5
  . ta=a1, t=a2, n=a3
  .loop i=0 to n
    . p=ta[i]
    .if.text.equal t, p ; p, t
      return i
    .end
  .endl
endf -1

macro .if.text.array.equal ta, t {
  text.array.equal ta, t, ta#.$
  .if r0<>-1
}

function text.array.begins, ta, t, n
  alias ta=v1, t=v2,\
   n=v3, i=v4, p=v5
  . ta=a1, t=a2, n=a3
  .loop i=0 to n
    . p=ta[i]
    .if.text.begins p, t
      return i
    .end
  .endl
endf -1

macro .if.text.array.begins ta, t {
  text.array.begins ta, t, ta#.$
  .if r0<>-1
}

; 2-DO: insensitive compare and search

;;;;;;;;;;;;;;;;;; CONVERSIONS ;;;;;;;;;;;;;;;;;;;

; x2t(n, t) ; number to text

; convert unsigned 32BIT integer to text

function u2t, n, t
  alias n=a1,\
   x=a2, y=a3,\
   t=v1, s=v2, c=v3
  . t=a2, s=a2
  .if n=0             ; zero?
    . (u8) *t++=30h
    . (u8) *t=0
    return t
  .end
  .while n            ; until 0
    . y=n             ; dividend
    . x=1999999Ah     ; ((2^32)/10)+1
    . n=n-(n>>>30)    ; n=n-(n>>>30)
    umull c, n, n, x  ; n*reciprocal
    . x=n<<1         
    . x=x+(x<<2)
    . x=y-x           ; remainder
    . c=x+30h         ; c=(n%10)+'0'
    . (u8) *t++=c     ; *t++=c
  .endw
  . (u8) *t=0
  text.reverse s
endf t

; convert signed 32BIT integer to text

function i2t, n, t
  alias n=a1,\
   t=a2, sign=a3
  . sign=80000000h
  .if n&sign          ; negate?
    . -n
    . (u8) *t++=2Dh   ; prepend '-'
  .end
  u2t                 ; convert
endf

; convert 32BIT hexadecimal number to text

function h2t, n, t
  alias t=v1, s=v2,\
   n=v3, x=v4, hex=v5
  . n=a1, t=a2, s=t
  . hex=_hex
  .if n=0             ; zero?
    . (u8) *t++=30h
    . (u8) *t=0
    return t
  .end
  .while n            ; *t++=*(hex+(n&(16-1)))
    . x=n&15
    ldrb x, [hex, x]  ; (u8) x=hex[x]
    . (u8) *t++=x
    . n>>>4           ; n/16
  .endw
  . (u8) *t=0
  text.reverse s
  return t
  _hex: db \
   '0123456789ABCDEF' ; 16 bytes
endf t

; convert 32BIT binary number to text

function b2t, n, t
  alias t=v1, s=v2,\
   n=v3, x=v4
  . n=a1, t=a2, s=t
  .if n=0             ; zero?
    . (u8) *t++=30h
    . (u8) *t=0
    return t
  .end
  .while n            ; *t++=(n&1)+'0'
    . x=n&1, x+30h
    . (u8) *t++=x
    . n>>>1           ; n/2
  .endw
  . (u8) *t=0
  text.reverse s
endf t

; t2x(t) ; text to number

; convert text to unsigned 32BIT integer

function t2u, t
  alias t=v1,\
   c=v2, n=v3
  . t=a1, (u8) c=*t
  .if c=30h           ; skip preceding
    .while c=30h      ; '0's...
      . (u8) c=*t++
    .endw
    . t--
  .end
  .if c=0             ; 0 value?
    return 0
  .end
  . n=0
  .forever
    . (u8) c=*t++
    .if c=0
      return n
    .end
    . n+(n<<2), n+n   ; n=n*10+*t++-'0'
    . n-30h, n+c
  .endfv
endf

; convert text to signed 32BIT integer

function t2i, t
  alias t=v1,\
   c=v2, negate=v3
  . t=a1, (u8) c=*t
  . negate=0
  .if c='-'           ; negative?
    . t++, negate=1   ; skip
  .end
  t2u t
  .if negate
    . -a1
  .end
endf

; convert hexadecimal text to number

function t2h, t       ; text to hexadecimal
  alias t=v1,\
   c=v2, n=v3
  . t=a1, (u8) c=*t
  .if c=30h           ; skip preceding
    .while c=30h      ; '0's...
      . (u8) c=*t++
    .endw
    . t--
  .end
  .if c=0             ; 0 value?
    return 0
  .end
  . n=0
  .forever
    . (u8) c=*t++
    .if c=0
      return n
    .end
    . n<<4            ; n=n*16+c2h(*t++)
    .if c<=39h        ; 0-9
      . c-30h
    .else.if c>=97    ; a-f
      . c-57h
    .else             ; A-F
      . c-37h
    .end
    . n+c
  .endfv
endf

; convert binary text to number

function t2b, t       ; text to binary
  alias t=v1,\
   c=v2, n=v3
  . t=a1, (u8) c=*t
  .if c=30h           ; skip preceding
    .while c=30h      ; '0's...
      . (u8) c=*t++
    .endw
    . t--
  .end
  .if c=0             ; 0 value?
    return 0
  .end
  . n=0
  .forever
    . (u8) c=*t++
    .if c=0
      return n
    .end
    . n<<1, n+c       ; n=n*2+*t++-'0'
    . n-30h
  .endfv
endf

;;;;;;;;;;;;;;;;;;;;; PRINT ;;;;;;;;;;;;;;;;;;;;;;

; 2-DO: print formatted text to buffer

;;;;;;;;;;;;;;;;;;; PARSE TEXT ;;;;;;;;;;;;;;;;;;;

; skip while type and not 0

; get p=text.skip.while p, C.WHITE

function text.skip.while, t, type
  alias t=a1, type=a2,\
   c=a3, p=a4, n=v1
  .forever
    . (u8) c=*t
    .if c=0
      return 0
    .end
    .if c=0Ah
      . p=line.n, (u32) n=*p
      . n++, (u32) *p=n
    .end
    . p=TLT, p+c
    . (u8) c=*p, c&type
    .breakz
    . t++
  .endfv
  . (u8) c=*t
  .if c=0
    return 0
  .end
endf

; skip until type and while not 0

; get p=text.skip.until p, C.RETURN

function text.skip.until, t, type
  alias t=a1, type=a2,\
   c=a3, p=a4, n=v1
  .forever
    . (u8) c=*t
    .if c=0
      return 0
    .end
    .if c=0Ah
      . p=line.n, (u32) n=*p
      . n++, (u32) *p=n
    .end
    . p=TLT, p+c
    . (u8) c=*p, c&type
    .breakn
    . t++
  .endfv
  . (u8) c=*t
  .if c=0
    return 0
  .end
endf

; copy while type and not 0

; get s=text.copy.while t, s, C.NAME

function text.copy.while, a, b, type
  alias a=a1, b=a2,\
   type=a3, c=a4, p=v1, n=v2
  .forever
    . (u8) c=*b
    .if c=0
      go .e
    .end
    .if c=0Ah
      . p=line.n, (u32) n=*p
      . n++, (u32) *p=n
    .end
    . p=TLT, p+c
    . (u8) c=*p, c&type
    .breakz
    . (u8) *a++=*b++
  .endfv
  .e:
  . (u8) *a=0, (u8) c=*b
  .if c=0
    return 0
  .end
endf b

; copy until type and while not 0

; get s=text.copy.until t, s, C.END

function text.copy.until, a, b, type
  alias a=a1, b=a2,\
   type=a3, c=a4, p=v1, n=v2
  .forever
    . (u8) c=*b
    .if c=0
      return 0
    .end
    .if c=0Ah
      . p=line.n, (u32) n=*p
      . n++, (u32) *p=n
    .end
    . p=TLT, p+c
    . (u8) c=*p, c&type
    .breakn
    . (u8) *a++=*b++
  .endfv
  . (u8) *a=0, (u8) c=*b
  .if c=0
    return 0
  .end
endf b

;;;;;;;;;;;;;;;;;; SOURCE, TOKEN ;;;;;;;;;;;;;;;;;

; safer, easier global skip/copy while/until

macro define.source {
 align 4
 void source.p, destiny.p, token.p
 integer token.type, token.value,\
  token.size, line.n, n.lines
}

macro set.source r
 { . r12=source.p, r11=r, (u32) *r12=r11 }

macro set.token r
 { . r12=token.p, r11=r, (u32) *r12=r11 }

macro set.stream s, t {
  set.source s
  set.token t
}

macro get.source s, t {
  . s=source.p, (u32) s=*s
  if ~t eq
    . t=token.p, (u32) t=*t
  end if
}

; 2-DO...

get.destiny fix get.console
set.destiny fix set.console
save.destiny fix save.console

macro get.token.p t
 { . t=token.p, (u32) t=*t }

macro set.token.x x, v
 {  . r12=token#x, r11=v, (u32) *r12=r11 }

macro set.token.type v { set.token.x .type, v }
macro set.token.class v { set.token.x .class, v }
macro set.token.value v { set.token.x .value, v }

macro get.token.value x
{ . x=token.value, (u32) x=*x }

; skip/copy while/until type...

function skip.while, type
  alias p=v1, type=v2
  . type=a1
  get.source p
  try p=text.skip.while p, type
  set.source p
endf p

function skip.until, type
  alias p=v1, type=v2
  . type=a1
  get.source p
  try p=text.skip.until p, type
  set.source p
endf p

function copy.while, type
  alias p=v1, t=v2, type=v3
  . type=a1
  get.source p, t
  try p=text.copy.while t, p, type
  set.source p
endf p

function copy.until, type
  alias p=v1, t=v2, type=v3
  . type=a1
  get.source p, t
  try p=text.copy.until t, p, type
  set.source p
endf p

macro skip.space   { skip.while C.SPACE }
macro skip.white   { skip.while C.WHITE }
macro skip.comment { skip.until C.RETURN }

function skip.all
  alias s=v1, c=v2
  .white:
  try skip.white
  get.source s
  . (u8) c=*s
  .if c=0
    return 0
  .end
  .if c=SEMI.C
    try skip.comment
    go .white
  .end
endf 1

; skip spaces then character. return 0
; if next c not key

function skip.c, key
  alias p=v1,\
   c=v2, key=v3
  . key=a1
  skip.space
  get.source p
  . (u8) c=*p
  .if c<>key
    return 0
  .end
  . p++
  set.source p
endf p

; ideas for parsing grammar:

; syntax: parse 'c' ; c=command/s

; '='      ; copy
; '>'      ; skip forward
; '<'      ; skip reverse
; '>1'     ; move right # times
; '<1'     ; move left # times
; '>ws'    ; skip while space/tab
; '>ur'    ; skip until return
; '>w_'    ; skip all whitespace
; '>w;'    ; skip all+comments
; '=u.'    ; copy until end/delimiter
; '=w#'    ; while digit
; '=w@'    ; while name/identifier
; '>w_=u.' ; skip whitespace then copy

; '=w(a-z|A-Z|0-9|_|.|?)'

Drawing Functions

; $$$$$$$$$$$$$$$$$$ MAGIC-ARM $$$$$$$$$$$$$$$$$$$
; *************** STAR^2 SOFTWARE ****************
; ?????????????????? DRAW.INC ????????????????????

; fast portable ARM graphics for RGB16 (5.6.5)

; vga.xy x, y  ; &vga[xy(x,y)]
; color.at     ; vga[xy(x,y)]

; clear.screen

; draw.pixel x, y, c
; draw.line.h x, y, w, c
; draw.line.v x, y, h, c

; draw.box x, y, w, h, c
; draw.box.t x, y, w, h, c, a
; draw.outline x, y, w, h, c

; draw.scanline x, y, w, p
; draw.scanline.t x, y, w, p
; draw.scanline.v x, y, w, p, c

; draw.bitmap x, y, w, h, p
; draw.bitmap.t x, y, w, h, p
; draw.bitmap.a x, y, w, h, p, a

; draw.fade x, y, w, h, c1, c2
; draw.shade x, y, w, h, c1, c2
; draw.black.bar x, y, w, h

;;;;;;;;;;;;;;;;;;;; DRAWING ;;;;;;;;;;;;;;;;;;;;;

; address &vga[(y*(screen.w*2))+(x*2)]...

function vga.xy, x, y
  . a3=SCREEN.PITCH, a4=a2*a3
  . a4+(a1<<1), a2=@vga, a1=a2+a4
endf

; erase screen with color/a1...

function clear.screen, c
  alias vga=a3,\
   w=a2, c=a1
  . vga=@vga, w=SCREEN.N
  .repeat w
    . (u16) *vga++=c
  .endr
endf

;;;;;;;;;;;;;;;; DRAW PIXEL, LINE ;;;;;;;;;;;;;;;;

; draw pixel: a1-a3=x/y/c...

function draw.pixel, x, y, c
  alias vga=a1, c=v1
  . c=a3
  vga.xy
  . (u16) *vga=c
endf

; draw horizontal line: a1-a4=x/y/w/c...

function draw.line.h, x, y, w, c
  alias vga=a1,\
   c=v1, w=v2
  . c=a4, w=a3
  vga.xy
  .repeat w
    . (u16) *vga++=c
  .endr
endf

; draw vertical line: a1-a4=x/y/h/c...

function draw.line.v, x, y, h, c
  alias vga=a1,\
   h=v1, c=v2, pitch=v3
  . c=a4, h=a3
  vga.xy
  . pitch=SCREEN.PITCH
  .repeat h
    . (u16) *vga=c, vga+pitch
  .endr
endf

;;;;;;;;;;;;;;; DRAW BOX, OUTLINE ;;;;;;;;;;;;;;;;

; draw solid rectangle: a1-v1=x/y/w/h/c...

function draw.box
  alias vga=a1, c=v1,\
   sw=v2, w=v3, h=v4,\
   pitch=a2, wb=a3
  . w=a3, sw=w, h=a4    ; save w/h
  vga.xy
  . pitch=SCREEN.PITCH  ; screen+row
  . wb=w<<1             ; w in bytes
  .repeat h             ; h # times
    . w=sw              ; reset saved
    .repeat w           ; w # times
      . (u16) *vga++=c  ; copy color
    .endr
    . vga+pitch, vga-wb ; advance
  .endr
endf

macro draw.box x, y, w, h, c {
  if ~x eq
    . v1=c, a1=x, a2=y, a3=w, a4=h
    draw.box
  end if
}

; draw rectangle outline...

function draw.outline
  alias c=v1,\
   x=v2, y=v3, w=v4, h=v5
  . x=a1, y=a2, w=a3, h=a4
  vga.xy
  draw.line.h x, y, w, c   ; top: x, y, w
  draw.line.v x, y, h, c   ; left: x, y, h
  . a1=x+w, a1--           ; right: x+w-1
  draw.line.v \
   a1, y+1, h-1, c
  . a2=y+h, a2--           ; bottom: y+h-1
  draw.line.h x, a2, w, c
endf

macro draw.outline x, y, w, h, c {
  if ~x eq
    . a1=x, a2=y, a3=w, a4=h, v1=c
  end if
  draw.outline
}

; draw rectangle with transparency...
; a1-a4=x/y/w/h. v5=color, v6=alpha

function draw.box.t
  alias vga=a1, c=v1,\
   w=v3, h=v4, sw=v2,\
   cl=v5, a=v6,\
   pitch=a2,\
   wb=a3, sc=v7
  . w=a3, sw=w, h=a4    ; save w/h
  vga.xy
  . pitch=SCREEN.PITCH  ; screen+row
  . wb=w<<1             ; w in bytes
  .repeat h             ; h # times
    . w=sw              ; reset saved
    .repeat w           ; w # times
     . (u16) c=*vga     ; screen color
      pusha
      mix c, cl, a      ; combine
      . c=a1
      popa
      . (u16) *vga++=c  ; copy
    .endr
    . vga+pitch, vga-wb ; advance
  .endr
endf

macro draw.box.t x, y, w, h, c, a {
  . a1=x, a2=y, a3=w, a4=h, v5=c, v6=a
  draw.box.t
}

;;;;;;;;;;;;;;;;; DRAW SCANLINE ;;;;;;;;;;;;;;;;;;

; draw 16BPP scanline. a1-a4=x/y/w/pixels...

function draw.scanline, x, y, w, p
  alias vga=a1,\          ; aliases
   p=v1, w=v2
  . p=a4, w=a3            ; save w
  vga.xy                  ; &vga(x,y)
  .repeat w               ; w # times
    . (u16) *vga++=*p++   ; copy pixel
  .endr
endf

; draw with transparent color...

function draw.scanline.t, x, y, w, p
  alias vga=a1,\     ; aliases
   c=a2, key=v2,\
   w=v3, p=v1
  . w=a3, p=a4       ; save w/p
  vga.xy             ; &vga(x,y)
   . (u16) key=*p    ; color at 0x0
  .repeat w          ; w # times
    . (u16) c=*p++   ; get source pixel
    .if c<>key       ; opaque?
      . (u16) *vga=c ; copy to screen
    .end
    . vga+2          ; next
  .endr
endf

; draw 16BPP variant scanline.
; a1-v1=x/y/w/c/pixels...

function draw.scanline.v
  alias vga=a1,\       ; aliases
   p=v1, c=v2, s=v3,\
   cl=v4, x=v5, w=v6,\
   n=v7
  . w=a3, cl=a4        ; save w, color
  vga.xy               ; &vga(x,y)
  .repeat w            ; w # times
    . (u16) c=*p++     ; get source pixel
    .if c=0            ; black, 100% invisible
      go .next
    .end
    . x=WHITE
    .if c=x            ; white, 100% visible
      . c=cl
      go .draw
    .end
    . (u16) s=*vga     ; else mixed
    . n=c&11111b, n<<3 ; alpha intensity
    .if n>=252         ; invisible
      go .next         ; skip
    .end
    .if n<=4           ; pure
      . c=cl
      go .draw
    .end
    pusha
    mix s, cl, n       ; combine
    . c=r0             ; screen+color
    popa
    .draw:
    . (u16) *vga=c
    .next:
    . vga+2            ; next
  .endr
endf

macro draw.scanline.v x, y, w, c, p {
  . a1=x, a2=y, a3=w, a4=c, v1=p
  draw.scanline.v
}

;;;;;;;;;;;;;;;;;; DRAW BITMAP ;;;;;;;;;;;;;;;;;;;

; draw 15/16BPP bitmap. a1-a4=x/y/w/h.
; v1=pixels...

function draw.bitmap
  alias vga=a1,\         ; aliases
   image=v1,\
   w=v2, h=v3, sw=v4,\   ; sizes, saved w
   pitch=v5, iwb=v6
  . w=a3, h=a4, sw=w     ; save size
  . pitch=SCREEN.PITCH   ; screen+image
  . iwb=w<<1             ; w in bytes
  vga.xy                 ; &vga(x,y)
  .repeat h              ; h # times
    . w=sw               ; get saved w
    .repeat w            ; w # times
      . (u16) \
       *vga++=*image++   ; copy pixel
    .endr
    . vga+pitch, vga-iwb ; advance
  .endr
endf

macro draw.bitmap p, x, y, w, h {
  if ~p eq
    . v1=p, a1=x, a2=y, a3=w, a4=h
  end if
  draw.bitmap
}

; draw bitmap with transparency
; by upper left pixel color (0x0).
; a1-a4=x/y/w/h. v1=pixels...

function draw.bitmap.t
  alias \                 ; aliases
   vga=a1, image=v1,\
   w=v2, h=v3, sw=v4,\    ; sizes, saved w
   pitch=v5, iwb=v6,\
   key=v7, c=a2
  . w=a3, h=a4, sw=w      ; save size
  . pitch=SCREEN.PITCH    ; screen+image
  . iwb=w<<1              ; w in bytes
  . (u16) key=*image      ; transparent color
  vga.xy                  ; &vga(x,y)
  .repeat h               ; h # times
    . w=sw
    .repeat w             ; w # times
      . (u16) c=*image++
      .if c<>key          ; opaque?
        . (u16) *vga++=c  ; copy pixel
      .else
        . vga+2           ; next
      .end
    .endr
    . vga+pitch, vga-iwb  ; advance
  .endr
endf

macro draw.bitmap.t p, x, y, w, h {
  if ~p eq
   . v1=p, a1=x, a2=y, a3=w, a4=h
  end if
  draw.bitmap.t
}

; draw bitmap with alpha in v8

function draw.bitmap.a
  alias \                 ; aliases
   vga=a1, image=v1,\
   w=v2, h=v3, sw=v4,\    ; sizes, saved w
   pitch=v5, iwb=v6,\
   key=v7, a=v8, c=r12
  . w=a3, h=a4, sw=w      ; save size
  . pitch=SCREEN.PITCH    ; screen+image
  . iwb=w<<1              ; w in bytes
  . (u16) key=*image      ; transparent color
  vga.xy                  ; &vga(x,y)
  .repeat h               ; h # times
    . w=sw
    .repeat w             ; w # times
      . (u16) c=*image++
      .if c<>key          ; opaque?
        pusha
        . (u16) a2=*vga
        . a1=c, a3=a
        mix
        . c=a1
        popa
        . (u16) *vga++=c  ; copy pixel
      .else
        . vga+2           ; next
      .end
    .endr
    . vga+pitch, vga-iwb  ; advance
  .endr
endf

macro draw.bitmap.a p, x, y, w, h, a {
  if ~p eq
   . v1=p, a1=x, a2=y, a3=w, a4=h, v8=a
  end if
  draw.bitmap.a
}

;;;;;;;;;;;;;;;;; DRAW GRADIENTS ;;;;;;;;;;;;;;;;;

; draw gradual vertical fade. parameters:
; a1-a4=x/y/w/h. v5-v6=c1/c2

function draw.fade
  alias vga=a1,\
   r=v1, g=v2, b=v3,\
   rn=v4, gn=v5, bn=v6,\
   c1=v7, c2=v8, h=r12

  pusha                 ; save a1-a4=x/y/w/h
  . c1=v5, c2=v6, h=a4  ; save colors+h
  delta8 c1, c2, 11, h  ; get deltas...
  . rn=a1
  delta8 c1, c2, 5, h
  . gn=a1
  delta8 c1, c2, 0, h
  . bn=a1, a1=c1        ; extract and scale
  get.rgb               ; r/g/b components
  . r=a1<<8
  . g=a2<<8
  . b=a3<<8
  popa                  ; restore x/y/w/h
  push a3-a4            ; save w/h
  vga.xy                ; x/y no longer needed
  pop a3-a4

  .repeat h             ; h # times
    pusha
    get c1=rgb r>>>8,\  ; c=(c>>>8)
     g>>>8, b>>>8
    popa
    . v8=a3             ; draw lines...
    .repeat v8          ; w # times
      . (u16) *vga++=c1 ; *vga++=c
    .endr
    . a2=SCREEN.PITCH
    . a1-(a3<<1), a1+a2 ; vga-(w*2)+pitch
    . r+rn, g+gn, b+bn  ; r/g/b+deltas
  .endr                 ; 8.8 fixed points
endf

macro draw.fade x, y, w, h, c1, c2 {
  . a1=x, a2=y, a3=w, a4=h, v5=c1, v6=c2
  draw.fade
}

; draw with vertical center fade:
; from c1 to c2 then c2 to c1.
; a1-a4=x/y/w/h. v5-v6=c1/c2

function draw.shade
  alias \
   x=a1, y=a2,\
   w=a3, h=a4,\
   c1=v5, c2=v6,\
   tmp=v7
  . h>>>1           ; h/2
  pusha
  draw.fade \
   x, y, w, h,\
   c1, c2
  popa
  . y+h, tmp=c1     ; move down
  . c1=c2, c2=tmp   ; exchange colors
  draw.fade \
   x, y, w, h,\
   c1, c2
endf

macro draw.shade x, y, w, h, c1, c2 {
  . a1=x, a2=y, a3=w, a4=h, v5=c1, v6=c2
  draw.shade
}

function draw.black.bar, x, y, w, h
 alias \
   x=a1, y=a2,\
   w=a3, h=a4,\
   c1=v5, c2=v6
  . h>>>1                 ; h/2
  pusha
  draw.fade x, y, w, h,\
   GRAY, GRAY25
  popa
  pusha
  . y+h                   ; move down
  draw.fade x, y, w, h,\
   BLACK, GRAY25
  popa
  pusha
  . h<<1
  draw.outline \          ; outline
   x, y, w, h, GRAY
  popa
  pusha
  draw.line.h \           ; highlight
   x, y, w, GRAY.L
  popa
endf