; ****************************************************************************
;
;                                  Compilation
;
; ****************************************************************************

#include "include.inc"

	.text

; ===== Tokenize table
; L094B

Token:
	.byte	'?'+0x80			; invalid token

	; Operators I (no leading space, no trailing space)
	.byte	'*','*'+0x80			; CH_DBLASTER	0x60	// ** (power) (double asterisk)
	.byte	'<','='+0x80			; CH_LTEQU	0x61	// <= (less or equal)
	.byte	'>','='+0x80			; CH_GREQU	0x62	// >= (greater or equal)
	.byte	'<','>'+0x80			; CH_NEQU	0x63	// <> (not equal)

	; Functions I, without parameter (no leading space, no trailing space)
	.byte	'I','N','K','E','Y','$'+0x80	; CH_INKEY	0x64	// INKEY$ read text from keyboard
	.byte	'M','E','M'+0x80		; CH_MEM	0x65	// MEM free memory
	.byte	'P','I'+0x80			; CH_PI		0x66	// PI pi number
	.byte	'R','N','D'+0x80		; CH_RND	0x67	// RND random number 0<=y<1

	; ... Start of tokens with trailing space character
	; Functions II, with parameter (no leading space character, print trailing space)
	.byte	'A','B','S'+0x80		; CH_ABS	0x68	// ABS absolute value
	.byte	'A','C','S'+0x80		; CH_ACS	0x69	// ACS arccosine
	.byte	'A','S','N'+0x80		; CH_ASN	0x6a	// ASN arcsine
	.byte	'A','T','N'+0x80		; CH_ATN	0x6b	// ATN arctangent
	.byte	'C','H','R','$'+0x80		; CH_CHR	0x6c	// CHR$ character from code 0..255
	.byte	'C','O','D','E'+0x80		; CH_CODE	0x6d	// CODE code from first character
	.byte	'C','O','S'+0x80		; CH_COS	0x6e	// COS cosine
	.byte	'E','X','P'+0x80		; CH_EXP	0x6f	// EXP natural exponent ex
	.byte	'I','N','T'+0x80		; CH_INT	0x70	// INT integer part
	.byte	'L','E','N'+0x80		; CH_LEN	0x71	// LEN length of text
	.byte	'L','N'+0x80			; CH_LN		0x72	// LN natural logarithm
	.byte	'S','G','N'+0x80		; CH_SGN	0x73	// SGN sign -1, 0, +1
	.byte	'S','I','N'+0x80		; CH_SIN	0x74	// SIN sine
	.byte	'S','Q','R'+0x80		; CH_SQR	0x75	// SQR square root
	.byte	'S','T','R','$'+0x80		; CH_STR	0x76	// STR$ string from number
	.byte	'T','A','N'+0x80		; CH_TAN	0x77	// TAN tangent
	.byte	'V','A','L'+0x80		; CH_VAL	0x78	// VAL number from string
	.byte	'N','O','T'+0x80		; CH_NOT	0x79	// NOT
	; PEEK unsupported
	; USR unsupported

	; ... Start of tokens with leading space character
	; Operators II (print leading space, print trailing space)
	.byte	'A','N','D'+0x80		; CH_AND	0x7a	// AND
	.byte	'O','R'+0x80			; CH_OR		0x7b	// OR

	; Statements
	.byte	'B','E','E','P'+0x80		; CH_BEEP	0x7c	// BEEP
	.byte	'C','L','E','A','R'+0x80	; CH_CLEAR	0x7d	// CLEAR
	.byte	'C','L','S'+0x80		; CH_CLS	0x7e	// CLS
	.byte	'C','O','N','T'+0x80		; CH_CONT	0x7f	// CONT
	.byte	'D','I','M'+0x80		; CH_DIM	0x80	// DIM
	.byte	'F','O','R'+0x80		; CH_FOR	0x81	// FOR
	.byte	'G','O','S','U','B'+0x80	; CH_GOSUB	0x82	// GOSUB
	.byte	'G','O','T','O'+0x80		; CH_GOTO	0x83	// GOTO
	.byte	'I','F'+0x80			; CH_IF		0x84	// IF
	.byte	'I','N','P','U','T'+0x80	; CH_INPUT	0x85	// INPUT
	.byte	'L','E','T'+0x80		; CH_LET	0x86	// LET
	.byte	'L','I','S','T'+0x80		; CH_LIST	0x87	// LIST
	.byte	'L','O','A','D'+0x80		; CH_LOAD	0x88	// LOAD
	.byte	'N','E','X','T'+0x80		; CH_NEXT	0x89	// NEXT
	.byte	'N','E','W'+0x80		; CH_NEW	0x8a	// NEW
	.byte	'P','A','U','S','E'+0x80	; CH_PAUSE	0x8b	// PAUSE
	.byte	'P','L','O','T'+0x80		; CH_PLOT	0x8c	// PLOT
	.byte	'P','R','I','N','T'+0x80	; CH_PRINT	0x8d	// PRINT
	.byte	'R','A','N','D'+0x80		; CH_RAND	0x8e	// RAND
	.byte	'R','E','M'+0x80		; CH_REM	0x8f	// REM
	.byte	'R','E','T','U','R','N'+0x80	; CH_RETURN	0x90	// RETURN
	.byte	'R','O','W','S'+0x80		; CH_ROWS	0x91	// ROWS
	.byte	'R','U','N'+0x80		; CH_RUN	0x92	// RUN
	.byte	'S','A','V','E'+0x80		; CH_SAVE	0x93	// SAVE
	.byte	'S','C','R','O','L','L'+0x80	; CH_SCROLL	0x94	// SCROLL
	.byte	'S','T','O','P'+0x80		; CH_STOP	0x95	// STOP
	.byte	'U','N','P','L','O','T'+0x80	; CH_UNPLOT	0x96	// UNPLOT

	; Specials
	.byte	'S','T','E','P'+0x80		; CH_STEP	0x97	// STEP
	.byte	'T','H','E','N'+0x80		; CH_THEN	0x98	// THEN
	.byte	'T','O'+0x80			; CH_TO		0x99	// TO
	; ... Stop of tokens with leading space character
	.byte	'A','T'+0x80			; CH_AT		0x9a	// AT
	.byte	'T','A','B'+0x80		; CH_TAB	0x9b	// TAB

	.balign 2	; to avoid linker error "warning: internal error: out of range error" must be at the end of unaligned tables

; ===== Statements executions

.global CmdBeg
CmdBeg:

;#Cmdine CH_BEEP	0x7b	// BEEP
CmdBeep:
	ret

;#Cmdine CH_CLEAR	0x7c	// CLEAR
CmdClear:
	ret

;#Cmdine CH_CLS		0x7d	// CLS
CmdCls:
	ret

;#Cmdine CH_CONT	0x7e	// CONT
CmdCont:
	ret

;#Cmdine CH_DIM		0x7f	// DIM
CmdDim:
	ret

;#Cmdine CH_FOR		0x80	// FOR
CmdFor:
	ret

;#Cmdine CH_GOSUB	0x81	// GOSUB
CmdGoSub:
	ret

;#Cmdine CH_GOTO	0x82	// GOTO
CmdGoTo:
	ret

;#Cmdine CH_IF		0x83	// IF
CmdIf:
	ret

;#Cmdine CH_INPUT	0x84	// INPUT
CmdInput:
	ret

;#Cmdine CH_LET		0x85	// LET
CmdLet:
	ret

;#Cmdine CH_LIST	0x86	// LIST
CmdList:
	ret

;#Cmdine CH_LOAD	0x87	// LOAD
CmdLoad:
	ret

;#Cmdine CH_MEM		0x88	// MEM
CmdMem:
	ret

;#Cmdine CH_NEXT	0x89	// NEXT
CmdNext:
	ret

;#Cmdine CH_NEW		0x8a	// NEW
CmdNew:
	ret

;#Cmdine CH_PAUSE	0x8b	// PAUSE
CmdPause:
	ret

;#Cmdine CH_PLOT	0x8c	// PLOT
CmdPlot:
	ret

;#Cmdine CH_PRINT	0x8d	// PRINT
CmdPrint:
	ret

;#Cmdine CH_RAND	0x8e	// RAND
CmdRand:
	ret

;#Cmdine CH_REM		0x8f	// REM
CmdRem:
	ret

;#Cmdine CH_RETURN	0x90	// RETURN
CmdReturn:
	ret

;#Cmdine CH_ROWS	0x91	// ROWS
CmdRows:
	ret

;#Cmdine CH_RUN		0x92	// RUN
CmdRun:
	ret

;#Cmdine CH_SAVE	0x93	// SAVE
CmdSave:
	ret

;#Cmdine CH_SCROLL	0x94	// SCROLL
CmdScroll:
	ret

;#Cmdine CH_STOP	0x95	// STOP
CmdStop:
	ret

;#Cmdine CH_UNPLOT	0x96	// UNPLOT
CmdUnplot:
	ret

; ===== Statements definitions
; Note: Cannot use labels inside the table - label alignes address pointer to word.

; Classes of operands
#define CLS_END		0x00	// no futher operands (terminates definition)
#define CLS_VAR		0x01	// a variable is required
#define CLS_EXP		0x02	// an expression, numeric of string, must follow (terminates definition)
#define CLS_NEXP0	0x03	// a numeric expression may follow, else default to zero (terminates definition)
#define CLS_VAR1	0x04	// a single character variable must follow
#define CLS_CUSTOM	0x05	// syntax checked by routine (terminates definition)
#define CLS_NEXP	0x06	// a numeric expression must follow

.global DefTabBeg
DefTabBeg:

;#define CH_BEEP	0x7b	// BEEP
#define DF_BEEP 0
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CH_COMMA	; ','
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdBeep

;#define CH_CLEAR	0x7c	// CLEAR
#define DF_CLEAR DF_BEEP+6
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdClear

;#define CH_CLS		0x7d	// CLS
#define DF_CLS DF_CLEAR+3
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdCls

;#define CH_CONT	0x7e	// CONT
#define DF_CONT DF_CLS+3
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdCont

;#define CH_DIM		0x7f	// DIM
#define DF_DIM DF_CONT+3
	.byte	CLS_CUSTOM	; syntax checked by routine (terminates definition)
	.word	CmdDim

;#define CH_FOR		0x80	// FOR
#define DF_FOR DF_DIM+3
	.byte	CLS_VAR1	; a single character variable must follow
	.byte	CH_EQU		; '='
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CH_TO		; TO
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CLS_CUSTOM	; syntax checked by routine (terminates definition)
	.word	CmdFor

;#define CH_GOSUB	0x81	// GOSUB
#define DF_GOSUB DF_FOR+8
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdGoSub

;#define CH_GOTO	0x82	// GOTO
#define DF_GOTO DF_GOSUB+4
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdGoTo

;#define CH_IF		0x83	// IF
#define DF_IF DF_GOTO+4
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CH_THEN		; THEN
	.byte	CLS_CUSTOM	; syntax checked by routine (terminates definition)
	.word	CmdIf

;#define CH_INPUT	0x84	// INPUT
#define DF_INPUT DF_IF+5
	.byte	CLS_VAR		; a variable is required
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdInput

;#define CH_LET		0x85	// LET
#define DF_LET DF_INPUT+4
	.byte	CLS_VAR		; a variable is required
	.byte	CH_EQU		; '='
	.byte	CLS_EXP		; an expression, numeric of string, must follow (terminates definition)

;#define CH_LIST	0x86	// LIST
#define DF_LIST DF_LET+3
	.byte	CLS_NEXP0	; a numeric expression may follow, else default to zero (terminates definition)
	.word	CmdList

;#define CH_LOAD	0x87	// LOAD
#define DF_LOAD DF_LIST+3
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdLoad

;#define CH_MEM		0x88	// MEM
#define DF_MEM DF_LOAD+3
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdMem

;#define CH_NEXT	0x89	// NEXT
#define DF_NEXT DF_MEM+3
	.byte	CLS_VAR1	; a single character variable must follow
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdNext

;#define CH_NEW		0x8a	// NEW
#define DF_NEW DF_NEXT+4
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdNew

;#define CH_PAUSE	0x8b	// PAUSE
#define DF_PAUSE DF_NEW+3
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdPause

;#define CH_PLOT	0x8c	// PLOT
#define DF_PLOT DF_PAUSE+4
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CH_COMMA	; ','
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdPlot

;#define CH_PRINT	0x8d	// PRINT
#define DF_PRINT DF_PLOT+6
	.byte	CLS_CUSTOM	; syntax checked by routine (terminates definition)
	.word	CmdPrint

;#define CH_RAND	0x8e	// RAND
#define DF_RAND DF_PRINT+3
	.byte	CLS_NEXP0	; a numeric expression may follow, else default to zero (terminates definition)
	.word	CmdRand

;#define CH_REM		0x8f	// REM
#define DF_REM DF_RAND+3
	.byte	CLS_CUSTOM	; syntax checked by routine (terminates definition)
	.word	CmdRem

;#define CH_RETURN	0x90	// RETURN
#define DF_RETURN DF_REM+3
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdReturn

;#define CH_ROWS	0x91	// ROWS
#define DF_ROWS DF_RETURN+3
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdRows

;#define CH_RUN		0x92	// RUN
#define DF_RUN DF_ROWS+4
	.byte	CLS_NEXP0	; a numeric expression may follow, else default to zero (terminates definition)
	.word	CmdRun

;#define CH_SAVE	0x93	// SAVE
#define DF_SAVE DF_RUN+3
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdSave

;#define CH_SCROLL	0x94	// SCROLL
#define DF_SCROLL DF_SAVE+3
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdScroll

;#define CH_STOP	0x95	// STOP
#define DF_STOP DF_SCROLL+3
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdStop

;#define CH_UNPLOT	0x96	// UNPLOT
#define DF_UNPLOT DF_STOP+3
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CH_COMMA	; ','
	.byte	CLS_NEXP	; a numeric expression must follow
	.byte	CLS_END		; no futher operands (terminates definition)
	.word	CmdUnplot

#define DF_ENDTAB DF_UNPLOT+6

; ===== Statements definition table

.global DefTab
DefTab:
	.byte	DF_BEEP		; CH_BEEP	0x7b	// BEEP
	.byte	DF_CLEAR	; CH_CLEAR	0x7c	// CLEAR
	.byte	DF_CLS		; CH_CLS	0x7d	// CLS
	.byte	DF_CONT		; CH_CONT	0x7e	// CONT
	.byte	DF_DIM		; CH_DIM	0x7f	// DIM
	.byte	DF_FOR		; CH_FOR	0x80	// FOR
	.byte	DF_GOSUB	; CH_GOSUB	0x81	// GOSUB
	.byte	DF_GOTO		; CH_GOTO	0x82	// GOTO
	.byte	DF_IF		; CH_IF		0x83	// IF
	.byte	DF_INPUT	; CH_INPUT	0x84	// INPUT
	.byte	DF_LET		; CH_LET	0x85	// LET
	.byte	DF_LIST		; CH_LIST	0x86	// LIST
	.byte	DF_LOAD		; CH_LOAD	0x87	// LOAD
	.byte	DF_MEM		; CH_MEM	0x88	// MEM
	.byte	DF_NEXT		; CH_NEXT	0x89	// NEXT
	.byte	DF_NEW		; CH_NEW	0x8a	// NEW
	.byte	DF_PAUSE	; CH_PAUSE	0x8b	// PAUSE
	.byte	DF_PLOT		; CH_PLOT	0x8c	// PLOT
	.byte	DF_PRINT	; CH_PRINT	0x8d	// PRINT
	.byte	DF_RAND		; CH_RAND	0x8e	// RAND
	.byte	DF_REM		; CH_REM	0x8f	// REM
	.byte	DF_RETURN	; CH_RETURN	0x90	// RETURN
	.byte	DF_ROWS		; CH_ROWS	0x91	// ROWS
	.byte	DF_RUN		; CH_RUN	0x92	// RUN
	.byte	DF_SAVE		; CH_SAVE	0x93	// SAVE
	.byte	DF_SCROLL	; CH_SCROLL	0x94	// SCROLL
	.byte	DF_STOP		; CH_STOP	0x95	// STOP
	.byte	DF_UNPLOT	; CH_UNPLOT	0x96	// UNPLOT
;	.byte	DF_ENDTAB

	.balign 2	; to avoid linker error "warning: internal error: out of range error" must be at the end of unaligned tables

; ----------------------------------------------------------------------------
;                             Compilation
; ----------------------------------------------------------------------------

.global Comp
Comp:


; load code -> R24 (ignore command in case of empty row!)
; skip spaces


; ----- relative code

	cpi	r24,CH_UNPLOT+1		; max. code
	brcc	Comp3			; invalid code
	subi	r24,CH_BEEP		; subtract lowest code
	brcc	Comp4			; code is OK

; ----- error, invalid command code

Comp3:	ldi	r24,ERR_INVEXP
	rjmp	Error

; ----- pointer into table DefTab -> Z

Comp4:
	ldi	r30,lo8(DefTab)
	ldi	r31,hi8(DefTab)
	add	r30,r24
	adc	r30,R_ZERO

; ----- prepare address of definition -> Z

	lpm	r30,Z			; offset of definition
	clr	r31
	subi	r30,lo8(-(DefTabBeg))
	sbci	r31,hi8(-(DefTabBeg))
	rjmp	ScanGetParam

; ----- load pointer into parameter table -> Z

ScanLoop:
	ldd	r30,Y+DATA_PARADDR
	ldd	r31,Y+DATA_PARADDR+1

; ----- load next parameter -> R22

ScanGetParam:
	ld	r22,Z+

; ----- save new pointer

	std	Y+DATA_PARADDR,r30
	std	Y+DATA_PARADDR+1,r31




	ret

; ----------------------------------------------------------------------------
;                          Clear edit row (workspace)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R27..R24, R1, R0
; STACK: 4 bytes
; ----------------------------------------------------------------------------

.global EditClear
EditClear:
	ldd	r30,Y+DATA_EDITADDR
	ldd	r31,Y+DATA_EDITADDR+1
	ldd	r24,Y+DATA_EDITEND
	ldd	r25,Y+DATA_EDITEND+1
	sub	r24,r30
	sbc	r25,r31

; INPUT: R31:R30 (Z) = start address
;	 R25:R24 = number of bytes to delete (>= 0)
;	 R26 = start offset of first pointer to update (OFF_CURLINE,...)
; OUTPUT: R31:R30 (Z) = new end address
;	  R27:R26 (X) = old end address
; DESTROYS: R25, R24, R1, R0
; STACK: 4 bytes
	ldi	r26,OFF_STKADDR
	rjmp	MemDel

; ----------------------------------------------------------------------------
;                  Add byte to end of edit line (workspace)
; ----------------------------------------------------------------------------
; INPUT: R24 = byte
; DESTROYS: R31, R30, R27..R23, R1, R0
; STACK: 8 bytes
; ----------------------------------------------------------------------------

.global EditAdd
EditAdd:

	mov	r23,r24		; save byte

	clr	r25
	ldi	r24,1		; R25:R24 <- 1 (insert 1 byte)
	ldd	r30,Y+DATA_EDITEND
	ldd	r31,Y+DATA_EDITEND+1 ; Z <- end of edit row
	ldi	r26,OFF_STKADDR
; INPUT: R31:R30 (Z) = start address
;	 R25:R24 = number of bytes to insert (>= 0)
;	 R26 = start offset of first pointer to update (OFF_CURLINE,...)
; OUTPUT: R31:R30 (Z) = start address + number of bytes
;	  R27:R26 (X) = start address
; DESTROYS: R25, R24, R1, R0
; STACK: 6 bytes
	rcall	MemIns		; insert byte

	st	X,r23		; save byte
	ret

; ----------------------------------------------------------------------------
;                Reserve space at end of edit line (workspace)
; ----------------------------------------------------------------------------
; INPUT: R24 = number of bytes
; OUTPUT: R27:R26 (Z) = address of start of the space
; DESTROYS: R31, R30, R25, R24, R1, R0
; STACK: 6 bytes
; ----------------------------------------------------------------------------

.global EditIns
EditIns:

	clr	r25
	ldd	r30,Y+DATA_EDITEND
	ldd	r31,Y+DATA_EDITEND+1 ; Z <- end of edit row
	ldi	r26,OFF_STKADDR
; INPUT: R31:R30 (Z) = start address
;	 R25:R24 = number of bytes to insert (>= 0)
;	 R26 = start offset of first pointer to update (OFF_CURLINE,...)
; OUTPUT: R31:R30 (Z) = start address + number of bytes
;	  R27:R26 (X) = start address
; DESTROYS: R25, R24, R1, R0
; STACK: 6 bytes
	rjmp	MemIns		; reserve space

; ----------------------------------------------------------------------------
;                    Skip spaces from parameters
; ----------------------------------------------------------------------------
; DESTROYS: R24, R30, R31
; STACK: 6 bytes
; ----------------------------------------------------------------------------

.global ParSpc
ParSpc:
	rcall	ParChar		; read byte (STACK 4 bytes)
	brcs	ParSetAddr4	; no next byte
	cpi	r24,CH_SPC	; space?
	breq	ParSpc		; delete space

; ParRet must follow

; ----------------------------------------------------------------------------
;                Return byte from parameters (decrease pointer)
; ----------------------------------------------------------------------------
; DESTROYS: R30, R31
; OUTPUT: flag C = set if no next data
; STACK: 4 bytes
; ----------------------------------------------------------------------------

.global ParRet
ParRet:

	rcall	ParGetAddr	; get pointer to parameters (STACK 2 bytes)
	sbiw	r30,1

; ParSetAddr must follow (here is C flag clear)

; ----------------------------------------------------------------------------
;                       Set pointer to parameters
; ----------------------------------------------------------------------------
; INPUT: R31:R30 (Z) pointer
; STACK: 2 bytes
; ----------------------------------------------------------------------------

.global ParSetAddr
ParSetAddr:

	std	Y+DATA_PARADDR,r30
	std	Y+DATA_PARADDR+1,r31
ParSetAddr4:
	ret

; ----------------------------------------------------------------------------
;                       Get pointer to parameters
; ----------------------------------------------------------------------------
; OUTPUT: R31:R30 (Z) pointer
; STACK: 2 bytes
; ----------------------------------------------------------------------------

.global ParGetAddr
ParGetAddr:

	ldd	r30,Y+DATA_PARADDR
	ldd	r31,Y+DATA_PARADDR+1
	ret

; ----------------------------------------------------------------------------
;                    Read byte from parameters
; ----------------------------------------------------------------------------
; OUTPUT: R24 = byte
;	  flag C = set if no data
; DESTROYS: R30, R31
; STACK: 4 bytes
; ----------------------------------------------------------------------------

.global ParChar
ParChar:

; ----- read address -> Z

	rcall	ParGetAddr	; get pointer to parameters (STACK 2 bytes)

; ----- check end of text

	ldd	r24,Y+DATA_PAREND
	cp	r24,r30
	breq	ParChar8	; no data

; ----- load byte

	ld	r24,Z+

; ----- save new address

	rcall	ParSetAddr	; set pointer to parameters (STACK 2 bytes)
	clc			; clear C flag
	ret

; ----- no next byte

ParChar8:
	sec			; set C flag
ParChar9:
	ret

; ----------------------------------------------------------------------------
;                          Find token
; ----------------------------------------------------------------------------
; INPUT: R24 = token code
; OUTPUT: R31:R30 (Z) = token address in ROM (for invalid token returns '?')
;	  flag C = is set to display leading space character
; DESTROYS: R25, R0
; ----------------------------------------------------------------------------
; L0975

.global FindToken
FindToken:

; ----- token table -> Z (pointer to 1st token "?"+0x80 = invalid)

	ldi	r30,lo8(Token)
	ldi	r31,hi8(Token)

; ----- check maximal token index

	mov	r25,r24		; token
	cpi	r25,TOKEN_LAST+1 ; check max. token
	brcc	FindToken8	; invalid token

; ----- prepare token index -> R25

	subi	r25,TOKEN_FIRST	; first token
	brcs	FindToken8	; invalid token
	inc	r25		; skip first token

; ----- find start of next word -> Z

FindToken4:
	lpm	r0,Z+		; R25 <- get next character
	tst	r0		; check end of word
	brpl	FindToken4	; find mark 0x80

; ----- token counter

	dec	r25		; token counter
	brne	FindToken4	; next token

; ----- check token with leading space

	cpi	r24,TOKEN_FIRSTSPC ; first token with leading space
	brcs	FindToken8	; no leading space
	cpi	r24,TOKEN_LASTSPC+1 ; last token with leading space
	brcc	FindToken8	; no leading space

; ----- set carry - print leading space

	sec			; set carry
	ret

; ----- clear carry - do not print leading space

FindToken8:
	clc			; clear carry
FindToken9:
	ret

; ----------------------------------------------------------------------------
;                          Display token
; ----------------------------------------------------------------------------
; INPUT: R24 = token code
; DESTROYS: R31, R30, R27..R20, R1, R0
; ----------------------------------------------------------------------------
; L094B

.global DispToken
DispToken:

; ----- find token
; INPUT: R24 = token code
; OUTPUT: R31:R30 (Z) = token address in ROM (for invalid token returns '?')
;	  flag C = is set to display leading space character
; DESTROYS: R25, R0

	push	r24

	rcall	FindToken	; find token -> Z
	brcc	DispToken2	; no leading space

; ----- display leading space
; DESTROYS: R27..R23, R1, R0
; STACK: 12 bytes

	ldd	r25,Y+DATA_FLAGS ; get flags
	andi	r25,B0		; suppress leadings space?
	brne	DispToken2	; suppress leading space
	rcall	DispSpc		; display space

; ----- display token
; INPUT: R31:R30 (Z) = text in ROM
; OUTPUT: R24 = last character (with bit 7 set)
;	  R31:R30 (Z) = pointer behind end of text
; DESTROYS: R27..R23, R21, R20, R1, R0
; STACK: 14 bytes
DispToken2:
	rcall	DispText	; display token

; ----- check trailing space

	pop	r25		; token code
	cpi	r25,TOKEN_FIRSTTRAIL ; check first trailing token
	brcs	FindToken9	; no trailing space

; ----- display trailing space (suppress following leading space)

	ldd	r25,Y+DATA_FLAGS ; get flags
	ori	r25,B0		; suppress leadings space
	std	Y+DATA_FLAGS,r25 ; set flags
	rjmp	DispSpc		; display space

; ----------------------------------------------------------------------------
;                            Tokenize row
; ----------------------------------------------------------------------------

.global Tokenize
Tokenize:

; ----- clear edit row
; DESTROYS: R31, R30, R27..R24, R1, R0
; STACK: 4 bytes
	rcall	EditClear

; ----- reduce memory
; DESTROYS: R31, R30, R27..R18, R1, R0
; STACK: 8 bytes
	rcall	DispReduce

; ----- prepare read pointer
; OUTPUT: R31:R30 (ZH:ZL) = row address in VRAM (= points to length of row)
;	  R24 = row length
; DESTROYS: nothing
; STACK: 2 bytes

	rcall	DispLine	; get current row -> Z and length -> R24
	adiw	r30,1		; start of text
	rcall	ParSetAddr	; set pointer to parameters (STACK 2 bytes)
	add	r30,r24		; end of text
	std	Y+DATA_PAREND,r30 ; end of parameters

; ----- skip spaces

Tokenize2:
	rcall	ParSpc		; skip spaces (STACK 6 bytes)
	brcs	ParChar9	; end of text

; ----- load pointer -> R21:R20

	rcall	ParGetAddr	; get pointer (STACK 2 bytes)
	movw	r20,r30		; R21:R20 <- save pointer

; ----- prepare token table -> X

	ldi	r26,lo8(Token)
	ldi	r27,hi8(Token)
	ldi	r22,TOKEN_FIRST	; first token

; ----- compare one token

1:    	movw	r30,r26		; Z <- token pointer
	lpm	r25,Z+		; R25 <- character from token table
	movw	r26,r30		; X <- new token pointer

	mov	r22,r25		; R22 <- save token character
	andi	r25,0x7f	; clear bit 7

	rcall	ParChar		; load character -> R24
	brcs	2f		; no next character

	cp	r24,r25		; compare characters
	brne	2f		; difference
	andi	r22,B7		; last character?
	breq	1b		; next character

; Note: Here should be check end of token.
;  It would be needed with words AT/ATN,
;  but ATN precede AT, so it will be OK.

; ----- token found OK, save token
	
; INPUT: R24 = byte
; DESTROYS: R31, R30, R27..R23, R1, R0
; STACK: 8 bytes
	mov	r24,r22		; R24 <- token
	rcall	EditAdd		; add token to edit line
	rjmp	Tokenize2	; get next token

; ----- shift to next token

2:	movw	r30,r20		; Z <- read pointer
	rcall	ParSetAddr	; set pointer

	movw	r30,r26		; Z <- token pointer
	sbiw	r30,1		; return last character
3:	lpm	r25,Z+		; load next character
	andi	r25,B7		; last chatacter?
	breq	3b		; find end of token
	movw	r26,r30		; X <- next token

	inc	r22		; increase token index
	cpi	r22,TOKEN_LAST+1 ; check last token
	brne	1b		; compare next token

; ----- token not found






	ret
