;-----------------------------------------------------------------------------------------------
; MBasic v1.0
; Copyleft Sylvain Bizoirre 01-2001.
; Improved by Franck Charlet 10-2002.
; Please report any questions, bugs, enhancements at:
;
; sb@retromatique.com (Sylvain Bizoirre)
; or:
; hitchhikr@australia.edu (Franck Charlet)
;
;-----------------------------------------------------------------------------------------------
; V1.0 changes made by Franck Charlet:
; (From original Mini Basic)
;
;[NEW]	- Added LINE function: returns last used line number.
;	- Added END command: same as STOP.
;	- Added CONT command: continue to run program at current line after a break.
;	- ? char can now be used as a PRINT command alias.
;	- Added LET command: variable assignation wrapper.
;	- Program names are now in Windows standard (was DOS 8.3 previously).
;	- A program name can now be passed as command line argument
;	  (MBasic will load and run it directly).
;	- Changed WaitLoop procedure: now uses Sleep() api (cleaner/smaller).
;	- Added handling for REM command.
;	- Added HELP command: display the available commands and functions.
;	- Added Error 24 on TextCr: End of line expected.
;	- Added SGN function: Returns the sign of an expression.
;	- Added handling for floating point numbers:
;	  - Commas are denoted by a dot and scientific format is available.
;	  - FPU operations will only be used if necessary
;	    (And they are when at least one term in a level of an EXPR is a floating point)
;	  - FOR/NEXT commands doesn't handle fp yet.
;	- Added Error 25: Invalid floating point number.
;	- Modified available functions and commands to handle floating points ops.
;	- Added Error 26: Expressions stack space exhausted.
;	- Changed Error 3 to: Numeric value overflow.
;	- Added Error 27: Error in expression.
;	- Improved expressions parser:
;	  TERMS can now be prefixed with unlimited number of signs operators.
;	- Added Error 28: Integer value expected.
;	- Improved LIST command: user can now pass an (integer) expression
;	  as line number (Eg. LIST LINE is now a possible statement).
;	- Added SIN function.
;	- Added COS function.
;	- Added SQRT function.
;	- Added Error 29: Illegal char found.
;	- Added {PI} function.
;	- Added TAN function.
;	- Swapped / and \ operator: 
;	  / = floating point divide
;	  \ = integer divide
;	  (Modified Mr. PITMAN to agree with us).
;	- Added Error 30: '(' statement expected.
;	- Added Error 31: ')' statement expected.
;	- Removed the default padding of output in PRINT command.
;	- Added LOG function.
;	- Added LOGN function.
;	- Added ATN function (arc tangent).
;	- Added EXP function (antelogarithm).
;	- Added {E} function.
;	- Added SEC function: 1/cos(a).
;	- Added COSEC function: 1/sin(a).
;	- Added COTAN function: 1/tan(a).
;	- Added ASIN function: atn(a/sqrt(-a*a+1).
;	- Added ACOS function: atn(-a/sqrt(-a*a+1))+2*atn(1).
;	- Added ASEC function: atn(a/sqrt(a*a-1))+sgn(a-1)*(2*atn(1)).
;	- Added ACOSEC function: atn(a/sqrt(a*a-1))+(sgn(a)-1)*(2*atn(1)).
;	- Added ACOTAN function: atn(x)+2*atn(1).
;	- Added HSIN function: (exp(a)-exp(-a))/2.
;	- Added HCOS function: (exp(a)+exp(-a))/2.
;	- Added HTAN function: (exp(a)-exp(-a))/(exp(a)+exp(-a)).
;	- Added HSEC function: 2/(exp(a)+exp(-a)).
;	- Added HCOSEC function: 2/(exp(a)-exp(-a)).
;	- Added HCOTAN function: (exp(a)+exp(-a))/(exp(a)-exp(-a)).
;	- Added HASIN function: log(a+sqrt(a*a+1)).
;	- Added HACOS function: log(a+sqrt(a*a-1)).
;	- Added HATN function: log((1+a)/(1-a))/2.
;	- Added HASEC function: log((sqrt(-a*a+1)+1)/a).
;	- Added HACOSEC function: log((sgn(a)*sqrt(a*a+1)+1)/a).
;	- Added HCOATN function: log((a+1)/(a-1))/2.
;	- Added TICK function: returns GetTickCount.
;	  (can be useful to count elapsed time).
;	- Added TROFF/TRON commands:
;	  TROFF will stop escape key checking performed at every line
;	  while TRON will switch it on (default),
;	  to stop that checking will result of MUCH greater execution speed.
;	  Note: every RUN command switch it ON so the program will have
;	  to explicitly switch if OFF if more speed is required.
;	- Added FIX function.
;	- Added INT function.
;	- Added IIF function.
;	- Added THEN keyword recognition (optional).
;	- ENDIF command is now handled:
;	- Improved speed by changing instructions recognition mechanism.
;	  (Tables splitting to avoid too long searchs for keywords).
;	- NEW and LOAD commands now erases the variables and array.
;	- Added Error 32: Ifs stack space exhausted.
;	- Added Error 33: Unexpected statement.
;	- Added Error 34: Unexpected end of file.
;	- Added ELSE command.
;	- Added 64Kb of memory for PEEK and POKE to avoid text area overwriting.
;	- Added Error 35: Command not available in this context.
;	- Added {TRUE} and {FALSE} functions (returns 1 and 0 respectively).
;	- Added Error 36: '"' statement expected.
;	- Commands with arguments must be separated with space char now
;	  (Modified Mr. PITMAN again).
;	- Most keywords (like TO or STEP) must be clearly separated with a space char.
;	- Added handling for hexadecimal numbers (prefixed with "0x").
;	- Added INKEY function: returns pressed key value (non blocking).
;	- Major speed enhancement.
;	- Empty PRINT command is not allowed anymore.
;	- Removed \ char handling in PRINT command.
;	- Added Error 37: Argument expected.
;	- Variables can now contains strings.
;	- Addresses of variables and array can now be obtained
;	  by adding & char before their names.
;	  Ex: B=&A will put address of variable A into B.
;	- INPUT and GET commands now requires & prefix (variable reference)
;	  in their arguments.
;	- Added Error 38: Variable or array address expected.
;	- Added Error 39: Invalid use of address reference.
;	- Added Error 40: Invalid cell reference.
;	- Background color in COLOR command is now optional
;	  (leaving it unchanged if specified).
;	- Added Error 41: Datas memory overflow.
;	- Added PEEKW function: same as PEEK but returns a 16 bits value.
;	- Added PEEKL function: same as PEEK but returns a 32 bits value.
;	- Added Error 42: No argument allowed.
;	- Empty braces "()" are now accepted as functions parameters.
;	- INPUT command improved: it can now accept two arguments:
;	  First one can be a string then second one the variable address.
;	  Ex: INPUT "Enter a value: ",&A
;	  This will print the mentioned string before waiting user's input.
;	  (The string argument is optional).
;	- User can now enter a string value as result of INPUT command.
;	- Added Error 43: Strings space exhausted.
;	- Added Error 44: Numeric value expected.
;	- NEXT command now requires a variable address.
;	- Added CHR function: returns ASCII value of an integer expression.
;	  Note: Carriage return is chr(10).
;
;[FIX]	- Fixed console dimensions handling in Windows NT/2000.
;	- Fixed file loading procedure.
;	- Won't print zeros as negative numbers anymore.
;	- Fixed idiv sign extension and Divide By Zero faults in Divide procedure.
;	- Fixed RND(): won't accept negative and null numbers
;	  (also won't return negative results).
;	- GET command will report 0 on cursors or functions keys releasing
;	  (So, Pitman should be playable now).
;	- ConsoleCharNbr variable wasn't loaded if window was of the same size
;	  (So CLS command should work correctly now).
;	- Console handling will now correctly send non-keys events to void.
;	- Corrected handling of cursors/home/end/insert keys.
;	  (Also fixed keypress/release problem).
;	- Fixed EDIT command: won't freeze MBasic anymore
;	  due to negative number of chars to print.
;	- MBasic can now be started in full screen mode
;	  under Windows NT/2000 as well (NT uses a 43 lines gfx mode by default).
;	- Fixed PITMAN.MBI: T/R/M keys are now available in lower case too.
;	- LOAD and SAVE commands won't accept illegal Windows chars in filenames.
;	- Changed various commands to accept complete expressions as parameters.
;	- Now remember correct cursor insert mode shape.
;	- Tabulation char is now replaced with space.
;	- WINDOW EDIT command reported an error sometimes.
;	  (sizes are now corrected if necessary depending on the charset).
;	- Default editor colors are restored before prompt.
;
;[MISC]	- Cleaned up MBasic.txt.
;	- Changed the default colors.
;	- MBasic will now report last API error number.
;	- Introduced precompilation engine.
;
;-----------------------------------------------------------------------------------------------
; (Requires MASM32 V7 + MAsmEd v0.34 distribution to assemble)
; masmedbuild RELEASE
; 	CAPT [BINDIR]\ml.exe /c /coff "%1.asm"
; 	CAPT [BINDIR]\Link.exe /MERGE:.data=.text /MERGE:.rdata=.text /SECTION:.text,ERW /IGNORE:4078 /SUBSYSTEM:CONSOLE "%1.obj"
; masmedbuildend
; masmedbuild DEBUG
; 	CAPT [BINDIR]\ml.exe /Zd /Zi /c /coff "%1.asm"
; 	CAPT [BINDIR]\Link.exe /DEBUG /DEBUGTYPE:CV /MERGE:.data=.text /MERGE:.rdata=.text /SECTION:.text,ERW /IGNORE:4078 /SUBSYSTEM:CONSOLE "%1.obj"
; masmedbuildend

;-----------------------------------------------------------------------------------------------
; Program model
			.586			; Required for RDTSC instruction
			.model	flat,stdcall
			option	casemap:none	; Case sensitive

;-----------------------------------------------------------------------------------------------
; External includes
			include	\masm32\include\windows.inc
			include	\masm32\include\kernel32.inc
			include	\masm32\include\user32.inc
			include	\masm32\include\winmm.inc
			include	\masm32\include\masm32.inc

			includelib \masm32\lib\kernel32.lib
			includelib \masm32\lib\user32.lib
			includelib \masm32\lib\winmm.lib
			includelib \masm32\lib\masm32.lib

;-----------------------------------------------------------------------------------------------
; Some functions declarations
StringToFloat		proto	:ptr byte,:ptr qword
FloatToString		proto	:qword,:ptr char
PushOperStack		proto	:dword
PokeOperStack		proto	:dword
FillMem			proto	:dword,:dword,:dword
PutTabPointer		proto	:dword,:dword
PushIfStack		proto	:dword
PokeIfStack		proto	:dword
AddWrapOperator		proto	:byte
AddWrapInteger		proto	:dword
AddWrapFp		proto	:qword
AddWrapFnc		proto	:dword
AddWrapVar		proto	:dword
AddWrapArray		proto
AddWrapExprEnd		proto
AddWrapMinusSign	proto
AddWrapOpenBracket	proto
AddWrapCloseBracket	proto
AddWrapString		proto	:dword
AddWrapStringPadding	proto
AddWrapArrayAddress	proto
AddWrapVarAddress	proto	:dword

;-----------------------------------------------------------------------------------------------
; Macro to register a command
; .l: command len
; .b: command name
CMD			MACRO	CmdName:REQ
_CmdName		TEXTEQU	@SubStr(CmdName,2,@SizeStr(CmdName)-2)
			dd	@SizeStr(CmdName)-2
			ifnb	_CmdName
_CmdName		TEXTEQU	@CatStr(<!">,%_CmdName,<!">)
			db	_CmdName
			endif
			ENDM

; Macro to register a function
; .l: function len
; .b: function name
; .l: function proc
FNC			MACRO	FncName:REQ,FncProc:REQ
_FncName		TEXTEQU	@SubStr(FncName,2,@SizeStr(FncName)-2)
			dd	@SizeStr(FncName)-2
			ifnb	_FncName
_FncName		TEXTEQU	@CatStr(<!">,%_FncName,<!">)
			db	_FncName
			endif
			dd	offset FncProc
			ENDM

; Functions table terminator
FNCEND			MACRO	FncProc:REQ
			dd	0
			db	128
			dd	offset FncProc
			ENDM

;-----------------------------------------------------------------------------------------------
; Some constants
			.const

VAR_INTEGER		equ	0			; Integer MUST be 0
VAR_FLOAT		equ	1
VAR_STRING		equ	2
VAR_NOFNCARG		equ	3

FP_LESSTHAN		equ	01h
FP_EQUALTO		equ	40h

MEMSIZE			equ	0ffffh			; Size of memory allocated for Basic progs (here 64 Ko.)
MEMDATSIZE		equ	0ffffh			; Size of datas memory (64kb)
MEMSTRINGSIZE		equ	01fffeh			; Size of string memory (128kb)
MEMCOMSIZE		equ	01fffeh			; Size of precompiled commands (128kb)
MEMEXPRSIZE		equ	01fffeh			; Size of precompiled expressions (128kb)
ARRAY_NBR		equ	2000			; Number of cells for @(array)

EXECMODE_INTERPRET	equ	0
EXECMODE_PRECOMP	equ	1
EXECMODE_FULLCOM	equ	2

; Wrapped expressions tags
; Determine the nature of the following datas
WRAP_MINUSSIGN		equ	1		; inverted result
WRAP_EXPREND		equ	2
WRAP_OPENBRACKET	equ	3
WRAP_CLOSEBRACKET	equ	4

WRAP_FNC		equ	5
WRAP_VAR		equ	6
WRAP_ARRAY		equ	7
WRAP_STRING		equ	8
WRAP_VARADDRESS		equ	9
WRAP_ARRAYADDRESS	equ	0ah

WRAP_FP			equ	0bh
WRAP_INT		equ	0ch

; Determine the operation to perform with the datas
WRAP_PLUS		equ	0dh
WRAP_MINUS		equ	0eh
WRAP_DIV		equ	0fh
WRAP_INTDIV		equ	010h
WRAP_MUL		equ	011h
WRAP_EQU		equ	012h
WRAP_LOWER		equ	013h
WRAP_GREATER		equ	014h
WRAP_LOWEREQU		equ	015h
WRAP_GREATEREQU		equ	016h
WRAP_NOTEQU		equ	017h
WRAP_STRPADDING		equ	018h

WRAP_FNC_E		equ	0
WRAP_FNC_PI		equ	1
WRAP_FNC_ABS		equ	2
WRAP_FNC_ACOSEC		equ	3
WRAP_FNC_ACOS		equ	4
WRAP_FNC_ACOTAN		equ	5
WRAP_FNC_ASEC		equ	6
WRAP_FNC_ASIN		equ	7
WRAP_FNC_ATN		equ	8
WRAP_FNC_CHR		equ	09h
WRAP_FNC_COSEC		equ	0ah
WRAP_FNC_COS		equ	0bh
WRAP_FNC_COTAN		equ	0ch
WRAP_FNC_EXP		equ	0dh
WRAP_FNC_FALSE		equ	0eh
WRAP_FNC_FIX		equ	0fh
WRAP_FNC_HCOATN		equ	010h
WRAP_FNC_HACOSEC	equ	011h
WRAP_FNC_HACOS		equ	012h
WRAP_FNC_HASEC		equ	013h
WRAP_FNC_HASIN		equ	014h
WRAP_FNC_HTAN		equ	015h
WRAP_FNC_HCOSEC		equ	016h
WRAP_FNC_HCOS		equ	017h
WRAP_FNC_HSEC		equ	018h
WRAP_FNC_HSIN		equ	019h
WRAP_FNC_HCOTAN		equ	01ah
WRAP_FNC_HATN		equ	01bh
WRAP_FNC_IIF		equ	01ch
WRAP_FNC_INKEY		equ	01dh
WRAP_FNC_INT		equ	01eh
WRAP_FNC_LINE		equ	01fh
WRAP_FNC_LOGN		equ	020h
WRAP_FNC_LOG		equ	021h
WRAP_FNC_PEEK		equ	022h
WRAP_FNC_PEEKL		equ	023h
WRAP_FNC_PEEKW		equ	024h
WRAP_FNC_RND		equ	025h
WRAP_FNC_SEC		equ	026h
WRAP_FNC_SIN		equ	027h
WRAP_FNC_SIZE		equ	028h
WRAP_FNC_SGN		equ	029h
WRAP_FNC_SQRT		equ	02ah
WRAP_FNC_TAN		equ	02bh
WRAP_FNC_TICK		equ	02ch
WRAP_FNC_TRUE		equ	02dh

;-----------------------------------------------------------------------------------------------
; Following variables can be modified to change MBasic features and appearence
			.data
EditColors		dd	17h			; Editor colors
EditSizeX		dw	80			; Editor screen sizes
EditSizeY		dw	30
PromptChar		db	">"			; Char. used for prompt
PromptColor		db	06h			; Prompt color

;----------------------------------------------------------------------------------------------- 
			align	8
InkeyBuf		dd	0
InkeyBufRead		dd	0
GhostIFPos		dd	0

TestOverLap		OVERLAPPED <>

dtemp1			dd	0			; Temporary 32 bits Variables
dtemp2			dd	0
wtemp1			dw	0			; Temporary 16 bits Variables
wtemp2			dw	0
wtemp3			dw	0
			align	8
btemp1			db	0			; Temporary 8 bits variable 
			align	8
btemp2			db	0
			align	8
GosLineNum		dd	0			; Line number of previous GOSUB or 0 if not
			align	8
GosPtrTxt		dd	0			; Text pointer of previous GOSUB or 0
			align	8
GosStackPtr		dd	0			; GOSUB Stack pointer
			align	8
ForStackPtr		dd	0			; FOR..NEXT Stack pointer
			align	8
ForAdrVar		dd	0			; Address of FOR loop variable
			align	8
ForLimit		dd	0			; Higher value of FOR variable
			align	8
ForIncrement		dd	0			; STEP increment or 1
			align	8
ForLine			dd	0			; Line to go back with NEXT
			align	8
ForPtrTxt		dd	0			; Address to go back with NEXT

CursorShape		CONSOLE_CURSOR_INFO <>

; CONSOLE_SCREEN_BUFFER_INFO (used in various API)
Csbi_SizeX		dw	0			; Size of screen buffer (Width X, Hight Y)
Csbi_SizeY		dw	0
Csbi_CursorPositionX	dw	0			; Cursor position (Column X, row Y)
Csbi_CursorPositionY	dw	0
Csbi_Attributes		dw	0			; Colors attributes for foreground and background
Csbi_WindowLeft		dw	0			; Console screen coordinates in Console Screen Buffer
Csbi_WindowTop		dw	0			; (Structure SMALL_RECT)
Csbi_WindowRight	dw	0
Csbi_WindowBottom	dw	0
Csbi_MaximumWindowSizeX	dw	0
Csbi_MaximumWindowSizeY	dw	0
			align	8
ColorsCurrent		dd	0			; Current color used
			align	8
NbrBlock		db	20 dup (0)		; Used to store temp numbers
			align	8
EventBuffer		db	16 dup (0)		; Informations area for keyboard inputs 
FileHandle		dd	0			; Given by CreateFile API (LOAD, SAVE)
FileName		dd	0			; Filename block
CmdLine			dd	0
hStdIn			dd	0			; Standard input and output handles given by Windows
hStdOut			dd	0
ExprFinalResult		dd	0			; Final decision over an expression type
PtrTblFnc		dd	0			; Functions table
LineLen			dd	0			; Input Line Length
LineMaxLen		dd	0			; Maximum input line length allowed
LineNumber		dd	0			; Current used line number
LineNumberComp		dd	0
SaveLineNumber		dd	-1			; used line number reported by LINE()
LinePtr			dd	0			; Input line ptr
ConsoleCharNbr		dd	0			; Number of char. in current console screen
ExecMode		dd	0			; 0 = interpret mode
							; 1 = pre compilation
 							; 2 = full compilation
PrecompDirect		db	0
			align	8
RequiredSpace		db	0
			align	8
InsertMode		db	0			; Insert key flag in line editor (1 = Insert, 0 = Normal)
			align	8
WasFloat		db	0			; Last checked number was a floating point ?
			align	8
TrState			db	0			; State of TRACE ON/OFF
			db	0
ClearInput		db	10 dup (" ")		; Fill input area with spaces
			db	0
			align	8
SMALL_RECT_Left		dw	0			; Coordinates of console screen in Console Screen Buffer
SMALL_RECT_Top		dw	0
SMALL_RECT_Right	dw	0
SMALL_RECT_Bottom	dw	0
			align	8
WindowCmdParmsPos	dd	0
WindowCmdParms2Pos	dd	0
WindowCmdParmsValue	dd	0
Sstack			dd	0			; Here is saved the stack pointer
			align	8
DblTemp			db	24 dup (0)
			align	8
szTemp			db	18 dup (0)
			align	8
ExpFPUState1		dw	0			; Used to handle FPU control word
			align	8
ExpFPUState2		dw	0
			align	8
FpIntLoad		dd	0
			align	8
EConst			dq	2.7182818284590452	; Hardcoded constant (shouldn't change)
			align	8
ten			dq	10.0
			align	8
ten16			dq	1.0e16
			align	8
ten_1			dt	1.0e1
			dt	1.0e2
			dt	1.0e3
			dt	1.0e4
			dt	1.0e5
			dt	1.0e6
			dt	1.0e7
			dt	1.0e8
			dt	1.0e9
			dt	1.0e10
			dt	1.0e11
			dt	1.0e12
			dt	1.0e13
			dt	1.0e14
			dt	1.0e15
ten_16			dt	1.0e16
			dt	1.0e32
			dt	1.0e48
			dt	1.0e64
			dt	1.0e80
			dt	1.0e96
			dt	1.0e112
			dt	1.0e128
			dt	1.0e144
			dt	1.0e160
			dt	1.0e176
			dt	1.0e192
			dt	1.0e208
			dt	1.0e224
			dt	1.0e240
ten_256			dt	1.0e256
			dt	1.0e512
			dt	1.0e768
			dt	1.0e1024
			dt	1.0e1280
			dt	1.0e1536
			dt	1.0e1792
			dt	1.0e2048
			dt	1.0e2304
			dt	1.0e2560
			dt	1.0e2816
			dt	1.0e3072
			dt	1.0e3328
			dt	1.0e3584
			dt	1.0e4096
			dt	1.0e4352
			dt	1.0e4608
			dt	1.0e4864

; --- Virtual memory Pointers
			align	8
PrintBuffer		dd	0			; Print buffer
ForNextStack		dd	0			; FOR..NEXT stack (5 Dwords * 16 FOR..NEXT levels)
GosubStack		dd	0			; GOSUB stack (2 Dwords * 16 GOSUB levels)
TxtStart		dd	0			; Basic text area
TxtEnd			dd	0			; End of Basic Text pointer
DatStart		dd	0			; Datas area
DatEnd			dd	0			; End of datas area
CompStart		dd	0			; Running text area
CompEnd			dd	0			; End of Running Text pointer
CompExpr		dd	0
CompExprPtr		dd	0
SaveCompExprPtr		dd	0
CompExprEnd		dd	0
CompPtr			dd	0			; Running text area position
DCompStart		dd	0			; Editor mode text area
DCompExpr		dd	0
CompIFExpr		dd	0
CompIFExprEnd		dd	0
PreCompLine		dd	0
PreCompLen		dw	0
PreCompNbr		dw	0
IfStack			dd	0			; Stack for if commands
IfStackPtr		dd	0
LineNum			dd	0			; 2 bytes before ASCII LineBuffer to save space for binary line number (4 bytes)
LineBuffer		dd	0			; Input line buffer (must be after text area)
FltString		dd	0			; Floating points temporary storage
Flt1			dq	1.0			; Some spaces for floating points
Flt2			dq	2.0			; operations
FltGarb			dq	0.0
FltGarb2		dq	0.0
FltGarb3		dq	0.0
FltGarb4		dq	0.0
FltGarbSgn		dq	0.0
FltVal			dq	0.0
FltTerm1		dq	0.0
FltTerm2		dq	0.0
OperStack		dd	0			; expressions stack
OperStackPtr		dd	0
LitStrings		dd	0			; 128k reserved for literal strings
LitStringsPtr		dd	0
FltFromInt		dd	0
FltToInt		dd	0
AZVar			dd	0			; Variables A to Z area (26 * Dword)
ArrayVar		dd	0			; Array variables area @(EXP) (ARRAY_NBR * Dword)

; Hex numbers conversion table (for 256 chars -1 = invalid value)
			align	8
HexTab			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	000h,001h,002h,003h,004h,005h,006h,007h,008h,009h,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,00ah,00bh,00ch,00dh,00eh,00fh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,00ah,00bh,00ch,00dh,00eh,00fh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
			db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
; --- Strings
			align	8
APIText			db	"Fatal API error: ",0
BasicCredits		db	"Written by Franck Charlet",0ah,0
BasicTitle		db	"MBasic v1.0",0ah,0
WinTitle		db	"MBasic v1.0",0
			align	8
FileExt			db	".MBI",0		; File extension automatically added
			align	8
Disp1Char		db	" ",0			; Only 1 char to display
			align	8
ReadyTxt		db	"Ready.",0
			align	8
CrLfTxt			db	13,10,0
EmptyChr		db	" ",0

; --- Errors strings
			align	8
ErrorTxt		db	"Error",0
ErrorIn			db	" in ",0
Colon			db	": ",0
Err0			db	"Syntax error",0
Err1			db	"Invalid Line number",0
Err2			db	"Numeric value overflow",0
Err3			db	"Subscript out of array range",0
Err4			db	"Out of memory",0
Err5			db	"TO expected after FOR statement",0
Err6			db	"Division by zero",0
Err7			db	"':' or end of line statement expected",0
Err8			db	"Keyword or variable expected",0
Err9			db	"'=' statement expected",0
Err10			db	"Line number not found",0
Err11			db	"More than 16 GOSUB levels",0
Err12			db	"RETURN without GOSUB",0
Err13			db	"Duplicate FOR..NEXT variable",0
Err14			db	"More than 16 FOR..NEXT levels",0
Err15			db	"NEXT without FOR",0
Err16			db	"NEXT variable doesn't match corresponding FOR",0
Err17			db	"',' statement expected",0
Err18			db	"Invalid argument",0
Err19			db	"Variable expected",0
Err20			db	"File not found",0
Err21			db	"File too large",0
Err22			db	"Not a MBasic file",0
Err23			db	"End of line expected",0
Err24			db	"Invalid floating point number",0
Err25			db	"Expressions stack space exhausted",0
Err26			db	"Error in expression",0
Err27			db	"Integer value expected",0
Err28			db	"Illegal char found",0
Err29			db	"'(' statement expected",0
Err30			db	"')' statement expected",0
Err31			db	"Ifs stack space exhausted",0
Err32			db	"Unexpected statement",0
Err33			db	"Unexpected end of file",0
Err34			db	"Command not available in this context",0
Err35			db	"'",34,"' statement expected",0
Err37			db	"Argument expected",0
Err38			db	"Variable or array address expected",0
Err39			db	"Invalid use of address reference",0
Err40			db	"Invalid cell reference",0
Err41			db	"Datas memory overflow",0
Err42			db	"No argument allowed",0
Err43			db	"Strings space exhausted",0
Err44			db	"Numeric value expected",0

; --- Help command strings
HelpDcommands		db	"Available direct mode commands:",0ah,0
HelpCommands		db	"Available language commands:",0ah,0
HelpFunctions		db	"Available functions:",0ah,0

;-----------------------------------------------------------------------------------------------
; Pre-conversion table
			align	8
PreCompTbl		dd	offset CMD_1_KEYWORD
			dd	offset CMD_2_KEYWORD
			dd	offset CMD_BEEP_KEYWORD
			dd	offset CMD_CLS_KEYWORD
			dd	offset CMD_COLOR_KEYWORD
			dd	offset CMD_ELSE_KEYWORD
			dd	offset CMD_ENDIF_KEYWORD
			dd	offset CMD_END_KEYWORD
			dd	offset CMD_FOR_KEYWORD
			dd	offset CMD_GET_KEYWORD
			dd	offset CMD_GOTO_KEYWORD
			dd	offset CMD_GOSUB_KEYWORD
IFPOS			equ	$
			dd	offset CMD_IF_KEYWORD
			dd	offset CMD_INPUT_KEYWORD
LETPOS			equ	$
			dd	offset CMD_LET_KEYWORD
			dd	offset CMD_LOCATE_KEYWORD
			dd	offset CMD_NEXT_KEYWORD
			dd	offset CMD_POKE_KEYWORD
			dd	offset CMD_PRINT_KEYWORD
REMPOS			equ	$
			dd	offset CMD_REM_KEYWORD
			dd	offset CMD_RETURN_KEYWORD
			dd	offset CMD_STOP_KEYWORD
			dd	offset CMD_TRON_KEYWORD
			dd	offset CMD_TROFF_KEYWORD
			dd	offset CMD_WAIT_KEYWORD
			dd	offset CMD_WINDOW_KEYWORD
			dd	offset CMD_CONT_KEYWORD
			dd	offset CMD_EDIT_KEYWORD
			dd	offset CMD_HELP_KEYWORD
			dd	offset CMD_LIST_KEYWORD
			dd	offset CMD_LOAD_KEYWORD
			dd	offset CMD_NEW_KEYWORD
			dd	offset CMD_RUN_KEYWORD
			dd	offset CMD_SAVE_KEYWORD
NBRPRECOMPTBL		equ	($-offset PreCompTbl)/4
COMPREM			equ	(REMPOS-offset PreCompTbl)/4
COMPIF			equ	(IFPOS-offset PreCompTbl)/4
COMPLET			equ	(LETPOS-offset PreCompTbl)/4

;-----------------------------------------------------------------------------------------------
; Direct commands help table
; 0=Carriage return
; -1=End of table
DCommandsHelp		dd	offset CMD_CONT_KEYWORD
			dd	offset CMD_EDIT_KEYWORD
			dd	offset CMD_HELP_KEYWORD
			dd	offset CMD_LIST_KEYWORD
			dd	offset CMD_LOAD_KEYWORD,0
			dd	offset CMD_NEW_KEYWORD
			dd	offset CMD_RUN_KEYWORD
			dd	offset CMD_SAVE_KEYWORD,0,0,-1

; Commands help table
CommandsHelp		dd	offset CMD_BEEP_KEYWORD
			dd	offset CMD_CLS_KEYWORD
			dd	offset CMD_COLOR_KEYWORD
			dd	offset CMD_ELSE_KEYWORD
			dd	offset CMD_ENDIF_KEYWORD,0
			dd	offset CMD_END_KEYWORD
			dd	offset CMD_FOR_KEYWORD
			dd	offset CMD_GET_KEYWORD
			dd	offset CMD_GOSUB_KEYWORD
			dd	offset CMD_GOTO_KEYWORD,0
			dd	offset CMD_IF_KEYWORD
			dd	offset CMD_INPUT_KEYWORD
			dd	offset CMD_LET_KEYWORD
			dd	offset CMD_LOCATE_KEYWORD
			dd	offset CMD_NEXT_KEYWORD,0
			dd	offset CMD_POKE_KEYWORD
			dd	offset CMD_PRINT_KEYWORD
			dd	offset CMD_REM_KEYWORD
			dd	offset CMD_RETURN_KEYWORD
			dd	offset CMD_STOP_KEYWORD,0
			dd	offset CMD_TROFF_KEYWORD
			dd	offset CMD_TRON_KEYWORD
			dd	offset CMD_WAIT_KEYWORD
			dd	offset CMD_WINDOW_KEYWORD,0,0,-1

; Functions help table
FunctionsHelp		dd	offset FNC_E_KEYWORD
			dd	offset FNC_FALSE_KEYWORD
			dd	offset FNC_PI_KEYWORD
			dd	offset FNC_TRUE_KEYWORD
			dd	offset FNC_ABS_KEYWORD,0
			dd	offset FNC_ACOS_KEYWORD
			dd	offset FNC_ACOSEC_KEYWORD
			dd	offset FNC_ACOTAN_KEYWORD
			dd	offset FNC_ASEC_KEYWORD
			dd	offset FNC_ASIN_KEYWORD,0
			dd	offset FNC_ATN_KEYWORD
			dd	offset FNC_COS_KEYWORD
			dd	offset FNC_CHR_KEYWORD
			dd	offset FNC_COSEC_KEYWORD
			dd	offset FNC_COTAN_KEYWORD,0
			dd	offset FNC_EXP_KEYWORD
			dd	offset FNC_FIX_KEYWORD
			dd	offset FNC_HCOATN_KEYWORD
			dd	offset FNC_HACOS_KEYWORD
			dd	offset FNC_HACOSEC_KEYWORD,0
			dd	offset FNC_HASEC_KEYWORD
			dd	offset FNC_HASIN_KEYWORD
			dd	offset FNC_HATN_KEYWORD
			dd	offset FNC_HCOS_KEYWORD
			dd	offset FNC_HCOSEC_KEYWORD,0
			dd	offset FNC_HCOTAN_KEYWORD
			dd	offset FNC_HTAN_KEYWORD
			dd	offset FNC_HSEC_KEYWORD
			dd	offset FNC_HSIN_KEYWORD
			dd	offset FNC_IIF_KEYWORD,0
			dd	offset FNC_INKEY_KEYWORD
			dd	offset FNC_INT_KEYWORD
			dd	offset FNC_LINE_KEYWORD
			dd	offset FNC_LOGN_KEYWORD
			dd	offset FNC_LOG_KEYWORD,0
			dd	offset FNC_PEEK_KEYWORD
			dd	offset FNC_PEEKL_KEYWORD
			dd	offset FNC_PEEKW_KEYWORD
			dd	offset FNC_RND_KEYWORD
			dd	offset FNC_SEC_KEYWORD,0
			dd	offset FNC_SGN_KEYWORD
			dd	offset FNC_SIN_KEYWORD
			dd	offset FNC_SIZE_KEYWORD
			dd	offset FNC_SQRT_KEYWORD
			dd	offset FNC_TAN_KEYWORD,0
			dd	offset FNC_TICK_KEYWORD,0,0,-1

;-----------------------------------------------------------------------------------------------
; Compiled commands in run mode
; Executes Err34 (Command not available in this context) if not available.
			align	8
RunTbl			dd	offset CMD_REMCOMP
			dd	offset CMD_PRINTCOMP
			dd	offset CMD_BEEPCOMP
			dd	offset CMD_CLSCOMP
			dd	offset CMD_COLORCOMP
			dd	offset CMD_ELSECOMP
			dd	offset CMD_ENDIFCOMP
			dd	offset CMD_STOPCOMP
			dd	offset CMD_FORCOMP
			dd	offset CMD_GETCOMP
			dd	offset CMD_GOTOCOMP
			dd	offset CMD_GOSUBCOMP
			dd	offset CMD_IFCOMP
			dd	offset CMD_INPUTCOMP
			dd	offset CMD_LETCOMP
			dd	offset CMD_LOCATECOMP
			dd	offset CMD_NEXTCOMP
			dd	offset CMD_POKECOMP
			dd	offset CMD_PRINTCOMP
			dd	offset CMD_REMCOMP
			dd	offset CMD_RETURNCOMP
			dd	offset CMD_STOPCOMP
			dd	offset CMD_TRONCOMP
			dd	offset CMD_TROFFCOMP
			dd	offset CMD_WAITCOMP
			dd	offset CMD_WINDOWCOMP
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34

; Compiled commands in direct mode
			align	8
DRunTbl			dd	offset E34
			dd	offset CMD_PRINTCOMP
			dd	offset E34
			dd	offset CMD_CLSCOMP
			dd	offset CMD_COLORCOMP
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset CMD_INPUTCOMP
			dd	offset CMD_LETCOMP
			dd	offset CMD_LOCATECOMP
			dd	offset E34
			dd	offset CMD_POKECOMP
			dd	offset CMD_PRINTCOMP
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset E34
			dd	offset CMD_WINDOWCOMP
			dd	offset CMD_CONTCOMP
			dd	offset CMD_EDITCOMP
			dd	offset CMD_HELPCOMP
			dd	offset CMD_LISTCOMP
			dd	offset CMD_LOADCOMP
			dd	offset CMD_NEWCOMP
			dd	offset CMD_RUNCOMP
			dd	offset CMD_SAVECOMP

; Compiled functions
RunFncTbl		dd	offset FNC_ECOMP
			dd	offset FNC_PICOMP
			dd	offset FNC_ABSCOMP
			dd	offset FNC_ACOSECCOMP
			dd	offset FNC_ACOSCOMP
			dd	offset FNC_ACOTANCOMP
			dd	offset FNC_ASECCOMP
			dd	offset FNC_ASINCOMP
			dd	offset FNC_ATNCOMP
			dd	offset FNC_CHRCOMP
			dd	offset FNC_COSECCOMP
			dd	offset FNC_COSCOMP
			dd	offset FNC_COTANCOMP
			dd	offset FNC_EXPCOMP
			dd	offset FNC_FALSECOMP
			dd	offset FNC_FIXCOMP
			dd	offset FNC_HCOATNCOMP
			dd	offset FNC_HACOSECCOMP
			dd	offset FNC_HACOSCOMP
			dd	offset FNC_HASECCOMP
			dd	offset FNC_HASINCOMP
			dd	offset FNC_HTANCOMP
			dd	offset FNC_HCOSECCOMP
			dd	offset FNC_HCOSCOMP
			dd	offset FNC_HSECCOMP
			dd	offset FNC_HSINCOMP
			dd	offset FNC_HCOTANCOMP
			dd	offset FNC_HATNCOMP
			dd	offset FNC_IIFCOMP
			dd	offset FNC_INKEYCOMP
			dd	offset FNC_INTCOMP
			dd	offset FNC_LINECOMP
			dd	offset FNC_LOGNCOMP
			dd	offset FNC_LOGCOMP
			dd	offset FNC_PEEKCOMP
			dd	offset FNC_PEEKLCOMP
			dd	offset FNC_PEEKWCOMP
			dd	offset FNC_RNDCOMP
			dd	offset FNC_SECCOMP
			dd	offset FNC_SINCOMP
			dd	offset FNC_SIZECOMP
			dd	offset FNC_SGNCOMP
			dd	offset FNC_SQRTCOMP
			dd	offset FNC_TANCOMP
			dd	offset FNC_TICKCOMP
			dd	offset FNC_TRUECOMP

; Commands not compiled (all commands must be accessible)
RunTblComp		dd	offset CMD_REM
			dd	offset CMD_PRINT
			dd	offset CMD_BEEP
			dd	offset CMD_CLS
			dd	offset CMD_COLOR
			dd	offset CMD_ELSE
			dd	offset CMD_ENDIF
			dd	offset CMD_STOP
			dd	offset CMD_FOR
			dd	offset CMD_GET
			dd	offset CMD_GOTO
			dd	offset CMD_GOSUB
			dd	offset CMD_IF
			dd	offset CMD_INPUT
			dd	offset CMD_LET
			dd	offset CMD_LOCATE
			dd	offset CMD_NEXT
			dd	offset CMD_POKE
			dd	offset CMD_PRINT
			dd	offset CMD_REM
			dd	offset CMD_RETURN
			dd	offset CMD_STOP
			dd	offset CMD_TRON
			dd	offset CMD_TROFF
			dd	offset CMD_WAIT
			dd	offset CMD_WINDOW
			dd	offset CMD_CONT
			dd	offset CMD_EDIT
			dd	offset CMD_HELP
			dd	offset CMD_LIST
			dd	offset CMD_LOAD
			dd	offset CMD_NEW
			dd	offset CMD_RUN
			dd	offset CMD_SAVE

;-----------------------------------------------------------------------------------------------
; ASCII commands table
			align	8
CMD_CONT_KEYWORD	equ	$
			CMD	"CONT"
			align	8
CMD_EDIT_KEYWORD	equ	$
			CMD	"EDIT"
			align	8
CMD_HELP_KEYWORD	equ	$
			CMD	"HELP"
			align	8
CMD_LIST_KEYWORD	equ	$
			CMD	"LIST"
			align	8
CMD_LOAD_KEYWORD	equ	$
			CMD	"LOAD"
			align	8
CMD_NEW_KEYWORD		equ	$
			CMD	"NEW"
			align	8
CMD_RUN_KEYWORD		equ	$
			CMD	"RUN"
			align	8
CMD_SAVE_KEYWORD	equ	$
			CMD	"SAVE"

			; --- End of direct only commands
			align	8
CMD_1_KEYWORD		equ	$
			CMD	"'"
			align	8
CMD_2_KEYWORD		equ	$
			CMD	"?"
			align	8
CMD_BEEP_KEYWORD	equ	$
			CMD	"BEEP"
			align	8
CMD_CLS_KEYWORD		equ	$
			CMD	"CLS"
			align	8
CMD_COLOR_KEYWORD	equ	$
			CMD	"COLOR"
			align	8
CMD_ELSE_KEYWORD	equ	$
			CMD	"ELSE"
			align	8
CMD_ENDIF_KEYWORD	equ	$
			CMD	"ENDIF"
			align	8
CMD_END_KEYWORD		equ	$
			CMD	"END"
			align	8
CMD_FOR_KEYWORD		equ	$
			CMD	"FOR"
			align	8
CMD_GET_KEYWORD		equ	$
			CMD	"GET"
			align	8
CMD_GOTO_KEYWORD	equ	$
			CMD	"GOTO"
			align	8
CMD_GOSUB_KEYWORD	equ	$
			CMD	"GOSUB"
			align	8
CMD_IF_KEYWORD		equ	$
			CMD	"IF"
			align	8
CMD_INPUT_KEYWORD	equ	$
			CMD	"INPUT"
			align	8
CMD_LET_KEYWORD		equ	$
			CMD	"LET"
			align	8
CMD_LOCATE_KEYWORD	equ	$
			CMD	"LOCATE"
			align	8
CMD_NEXT_KEYWORD	equ	$
			CMD	"NEXT"
			align	8
CMD_POKE_KEYWORD	equ	$
			CMD	"POKE"
			align	8
CMD_PRINT_KEYWORD	equ	$
			CMD	"PRINT"
			align	8
CMD_REM_KEYWORD		equ	$
			CMD	"REM"
			align	8
CMD_RETURN_KEYWORD	equ	$
			CMD	"RETURN"
			align	8
CMD_STOP_KEYWORD	equ	$
			CMD	"STOP"
			align	8
CMD_TRON_KEYWORD	equ	$
			CMD	"TRON"
			align	8
CMD_TROFF_KEYWORD	equ	$
			CMD	"TROFF"
			align	8
CMD_WAIT_KEYWORD	equ	$
			CMD	"WAIT"
			align	8
CMD_WINDOW_KEYWORD	equ	$
			CMD	"WINDOW"

;-----------------------------------------------------------------------------------------------
; Functions Table.
			align	8
TAB_FNC1		equ	$
FNC_E_KEYWORD		equ	$
			FNC	"{E}",FNC_E
FNC_FALSE_KEYWORD	equ	$
			FNC	"{FALSE}",FNC_FALSE
FNC_PI_KEYWORD		equ	$
			FNC	"{PI}",FNC_PI
FNC_TRUE_KEYWORD	equ	$
			FNC	"{TRUE}",FNC_TRUE
			FNCEND	Expr_GetVar

			align	8
TAB_FNCA		equ	$
FNC_ABS_KEYWORD		equ	$
			FNC	"ABS",FNC_ABS
FNC_ACOSEC_KEYWORD	equ	$
			FNC	"ACOSEC",FNC_ACOSEC
FNC_ACOS_KEYWORD	equ	$
			FNC	"ACOS",FNC_ACOS
FNC_ACOTAN_KEYWORD	equ	$
			FNC	"ACOTAN",FNC_ACOTAN
FNC_ASEC_KEYWORD	equ	$
			FNC	"ASEC",FNC_ASEC
FNC_ASIN_KEYWORD	equ	$
			FNC	"ASIN",FNC_ASIN
FNC_ATN_KEYWORD		equ	$
			FNC	"ATN",FNC_ATN
			FNCEND	Expr_GetVar

			align	8
TAB_FNCC		equ	$
FNC_CHR_KEYWORD		equ	$
			FNC	"CHR",FNC_CHR
FNC_COSEC_KEYWORD	equ	$
			FNC	"COSEC",FNC_COSEC
FNC_COS_KEYWORD		equ	$
			FNC	"COS",FNC_COS
FNC_COTAN_KEYWORD	equ	$
			FNC	"COTAN",FNC_COTAN
			FNCEND	Expr_GetVar

			align	8
TAB_FNCE		equ	$
FNC_EXP_KEYWORD		equ	$
			FNC	"EXP",FNC_EXP
			FNCEND	Expr_GetVar

			align	8
TAB_FNCF		equ	$
FNC_FIX_KEYWORD		equ	$
			FNC	"FIX",FNC_FIX
			FNCEND	Expr_GetVar

			align	8
TAB_FNCH		equ	$
FNC_HCOATN_KEYWORD	equ	$
			FNC	"HCOATN",FNC_HCOATN
FNC_HACOSEC_KEYWORD	equ	$
			FNC	"HACOSEC",FNC_HACOSEC
FNC_HACOS_KEYWORD	equ	$
			FNC	"HACOS",FNC_HACOS
FNC_HASEC_KEYWORD	equ	$
			FNC	"HASEC",FNC_HASEC
FNC_HASIN_KEYWORD	equ	$
			FNC	"HASIN",FNC_HASIN
FNC_HTAN_KEYWORD	equ	$
			FNC	"HTAN",FNC_HTAN
FNC_HCOSEC_KEYWORD	equ	$
			FNC	"HCOSEC",FNC_HCOSEC
FNC_HCOS_KEYWORD	equ	$
			FNC	"HCOS",FNC_HCOS
FNC_HSEC_KEYWORD	equ	$
			FNC	"HSEC",FNC_HSEC
FNC_HSIN_KEYWORD	equ	$
			FNC	"HSIN",FNC_HSIN
FNC_HCOTAN_KEYWORD	equ	$
			FNC	"HCOTAN",FNC_HCOTAN
FNC_HATN_KEYWORD	equ	$
			FNC	"HATN",FNC_HATN
			FNCEND	Expr_GetVar

			align	8
TAB_FNCI		equ	$
FNC_IIF_KEYWORD		equ	$
			FNC	"IIF",FNC_IIF
FNC_INKEY_KEYWORD	equ	$
			FNC	"INKEY",FNC_INKEY
FNC_INT_KEYWORD		equ	$
			FNC	"INT",FNC_INT
			FNCEND	Expr_GetVar

			align	8
TAB_FNCL		equ	$
FNC_LINE_KEYWORD	equ	$
			FNC	"LINE",FNC_LINE
FNC_LOGN_KEYWORD	equ	$
			FNC	"LOGN",FNC_LOGN
FNC_LOG_KEYWORD		equ	$
			FNC	"LOG",FNC_LOG
			FNCEND	Expr_GetVar

			align	8
TAB_FNCP		equ	$
FNC_PEEKL_KEYWORD	equ	$
			FNC	"PEEKL",FNC_PEEKL
FNC_PEEKW_KEYWORD	equ	$
			FNC	"PEEKW",FNC_PEEKW
FNC_PEEK_KEYWORD	equ	$
			FNC	"PEEK",FNC_PEEK
			FNCEND	Expr_GetVar

			align	8
TAB_FNCR		equ	$
FNC_RND_KEYWORD		equ	$
			FNC	"RND",FNC_RND
			FNCEND	Expr_GetVar

			align	8
TAB_FNCS		equ	$
FNC_SEC_KEYWORD		equ	$
			FNC	"SEC",FNC_SEC
FNC_SIN_KEYWORD		equ	$
			FNC	"SIN",FNC_SIN
FNC_SIZE_KEYWORD	equ	$
			FNC	"SIZE",FNC_SIZE
FNC_SGN_KEYWORD		equ	$
			FNC	"SGN",FNC_SGN
FNC_SQRT_KEYWORD	equ	$
			FNC	"SQRT",FNC_SQRT
			FNCEND	Expr_GetVar

			align	8
TAB_FNCT		equ	$
FNC_TAN_KEYWORD		equ	$
			FNC	"TAN",FNC_TAN
FNC_TICK_KEYWORD	equ	$
			FNC	"TICK",FNC_TICK
			FNCEND	Expr_GetVar

			align	8
TAB_FNCFALL		equ	$
			FNCEND	Expr_GetVar

;-----------------------------------------------------------------------------------------------
; TO keyword
			align	8
TBL_TO			equ	$
			FNC	"TO ",TOKEYWORD
			FNCEND	ErrorTO

;-----------------------------------------------------------------------------------------------
; STEP keyword
			align	8
TBL_STEP		equ	$
			FNC	"STEP ",STEPKEYWORD
			FNCEND	NOSTEPKEYWORD

;-----------------------------------------------------------------------------------------------
; EDIT keyword
			align	8
TBL_EDIT		equ	$
			FNC	"EDIT",WINDOW_EDIT
			FNCEND	WINDOW_XXX

;-----------------------------------------------------------------------------------------------
; For pre-compilation purposes only
			align	8
TBL_THENPRECOMP		equ	$
			FNC	"THEN",PreCompThenFound
			FNCEND	PreCompThenNotFound

;-----------------------------------------------------------------------------------------------
; For ghosting purposes only
;			align	8
;TBL_THENGHOSTIF		equ	$
;			FNC	"THEN",GhostThenFound
;			FNCEND	GhostThenNotFound

;-----------------------------------------------------------------------------------------------
; Relational operators
			align	8
TAB_REL_OP		equ	$
			FNC	">=",Op_GTEQ
			FNC	"<>",Op_NOTEQ
			FNC	">",Op_GT
			FNC	"=",Op_EQ
			FNC	"<=",Op_LESSEQ
			FNC	"<",Op_LESS
			FNCEND	Op_Unknown

;-----------------------------------------------------------------------------------------------
; MBasic code starts here.
			.code
start:
; Allocate necessary virtual memory for buffers, Basic text and @(array) 
; !!! ALLOCATION ORDER MUST NOT BE CHANGED !!!
    			mov	eax,704 + MEMSIZE + 2 + 256 + (26*16) + (ARRAY_NBR*16) + MAX_PATH + MAX_PATH + 80 + (256*4) + MEMDATSIZE + (256*4) +8 + (256*8) + MEMCOMSIZE + MEMEXPRSIZE + (256*4) + (256*4) + MEMSTRINGSIZE
    			invoke	VirtualAlloc,0,eax,MEM_COMMIT or MEM_RESERVE,PAGE_READWRITE
			test	eax,eax
			jz	DisplayError		; Api Error
			mov	PrintBuffer,eax
			add	eax,256			; Print buffer area
			mov	ForNextStack,eax
			add	eax,320			; FOR..NEXT stack area
			mov	GosubStack,eax
			add	eax,128			; Gosub stack area
			mov	TxtStart,eax
			add	eax,MEMSIZE		; Basic text area
			mov	LineNum,eax
			add	eax,2			; 2 bytes before ASCII line number
			mov	LineBuffer,eax
			add	eax,256			; Input line buffer area (256/line)
			mov	AZVar,eax
			add	eax,(26*16)		; Variables A to Z and @(array) areas
			mov	ArrayVar,eax
 			add	eax,(ARRAY_NBR*16)
			mov	FileName,eax		; Loaded program name
			add	eax,MAX_PATH
			mov	CmdLine,eax		; Passed program name in argument line
			add	eax,MAX_PATH
			mov	FltString,eax
			add	eax,80
			mov	OperStack,eax		; 256*4 bytes for expressions stack
			mov	OperStackPtr,eax
			add	eax,(256*4)
			mov	DatStart,eax		; POKE/PEEK memory (64kb)
			add	eax,MEMDATSIZE
			mov	DatEnd,eax
			mov	PtrTblFnc,eax
			add	eax,(256*4)
			mov	dword ptr [eax],0
			mov	dword ptr [eax+4],-1
			add	eax,8			; Padding for IfStack
			mov	IfStack,eax
			mov	IfStackPtr,eax
			add	eax,(256*8)
			mov	CompStart,eax
			mov	CompEnd,eax
			mov	CompPtr,eax
			add	eax,MEMCOMSIZE
			mov	CompExpr,eax		; Block for precompiled expressions
			mov	CompExprPtr,eax
			add	eax,MEMEXPRSIZE
			mov	DCompStart,eax		; Pre compiled direct commands
			add	eax,(256*4)
			mov	DCompExpr,eax		; precompiled expressions for direct commands
			add	eax,(256*4)
			mov	LitStrings,eax
			mov	LitStringsPtr,eax
			; Fill functions pointers table
			invoke	FillMem,PtrTblFnc,(256*4),offset TAB_FNCFALL
			mov	eax,PtrTblFnc
			mov	ebx,"{"
			mov	[eax+ebx*4],offset TAB_FNC1
			invoke	PutTabPointer,"a",offset TAB_FNCA
			invoke	PutTabPointer,"c",offset TAB_FNCC
			invoke	PutTabPointer,"e",offset TAB_FNCE
			invoke	PutTabPointer,"f",offset TAB_FNCF
			invoke	PutTabPointer,"h",offset TAB_FNCH
			invoke	PutTabPointer,"i",offset TAB_FNCI
			invoke	PutTabPointer,"l",offset TAB_FNCL
			invoke	PutTabPointer,"p",offset TAB_FNCP
			invoke	PutTabPointer,"r",offset TAB_FNCR
			invoke	PutTabPointer,"s",offset TAB_FNCS
			invoke	PutTabPointer,"t",offset TAB_FNCT
			; Retrieve command argument
			invoke	GetCL,1,CmdLine
			call	AllocCons		; And allocate a new one
			call	WinEdit			; Set window size and colors to standard MBasic editor
			mov	Sstack,esp		; Save current stack address
			cld				; Direction flag = 0, for strings transfer
			finit				; Initialize FPU
			mov	eax,TxtStart		; NEW command like
			mov	TxtEnd,eax
			mov	eax,CompStart
			mov	CompEnd,eax
			mov	eax,LitStrings
			mov	LitStringsPtr,eax
			mov	LineMaxLen,255		; Basic text line maximum length
			xor	al,al
			mov	edx,offset BasicTitle
			call	DispStr
			xor	al,al
			mov	edx,offset BasicCredits
			call	DispStr
			call	CrLf
			; Try to load and run specified file in command line (if any)
			mov	eax,CmdLine
			mov	bl,[eax]
			test	bl,bl
			jz	Ready			; Nothing: print ready.
			; Get parameter
			invoke	lstrcpy,FileName,CmdLine
			call	AppendMBasicExtension
			call	InitP
			call	AutoLoadFile
			jmp	CMD_RUNCOMP		; Run it
Ready:      		mov	eax,EditColors		; Set default editor colors
			mov	ColorsCurrent,eax
			; Init some variables and print 'Ready.' then prompt
			call	InitP			; Init. FOR..NEXT and GOSUB stacks pointers
			cmp	Csbi_CursorPositionX,0	; If cursor is not at top of a line;
			je	@F			; Print a CrLf before 'Ready'
			call	CrLf
@@:			mov	edx,offset ReadyTxt	; Display "Ready"
			xor	al,al			; ah > al
			call	DispStr
Prompt:			mov	eax,EditColors		; Set default editor colors
			mov	ColorsCurrent,eax
			cmp	Csbi_CursorPositionX,0	; Same as above but coming from different
			je	@F			; parts of the program
			call	CrLf
@@:			; Print prompt and wait for user input line. Basic variables are not cleared
			mov	esp,Sstack		; Stack pointer retrieve original value
			push	ColorsCurrent
			movzx	eax,PromptColor
			or	ColorsCurrent,eax	; Prompt color on current background color
			mov	edx,offset Disp1Char	; Print prompt
			mov	al,PromptChar
			mov	[edx],al
			xor	al,al
			call	DispStr
			pop	ColorsCurrent
			invoke	SetConsoleTextAttribute,hStdOut,dword ptr ColorsCurrent
			call	TestApiError		; Colors of next typed line
			mov	LinePtr,1		; Init line input pointers
			mov	LineLen,1
EDMode:			xor	eax,eax			; Nothing allowed in output (no auto-CR & scroll)
			invoke	SetConsoleMode,hStdOut,eax
			call	TestApiError
			mov	CursorShape.bVisible,1	; visible cursor
			call	SetConCursor
			call	TestApiError
			; Clear keyboard input buffer
			invoke	FlushConsoleInputBuffer,hStdIn
			call	LineInput		; Wait for user to type a line
			mov	eax,LineBuffer
			cmp	byte ptr [eax+1],13	; Empty line ?
			je	Prompt
			cmp	bl,27			; ESC in line input ?
			je	Prompt			; Yes, ignore last typed line   
			mov	edx,LineBuffer		; edx = top of typed line
			inc	edx
			mov	eax,LineLen
			add	eax,edx
			mov	ecx,eax			; ecx = End of typed line
			push	ecx
			push	edx
			call	Ucase			; Convert line to upper case
			pop	edx
			call	TstNum			; Line starts with a number ?
			pop	ecx
			test	ebx,ebx
			jnz	@F			; Yes, Insert, Cancel or replace line in Basic text
			jmp	DirectCmd		; No, execute direct command
; a line with a line number can : Be inserted in Basic text
;                               : Replace a line with same line number
;                               : Cancel a line with same line number (empty line)
@@:			cmp	ebx,100000		; Line number > 99999 ?
			jb	@F			; No, OK
			mov	edi,offset Err1		; Yes, Error 1 : <Invalid Line number>
			jmp	DispError
@@:			sub	edx,4			; edx = new top of line (1st.char after ASCII line num. - 4 bytes for binary line number)
			mov	[edx],ebx		; First top of line address = binary line number
			push	ecx			; Save end of line
			push	edx			; Save top of line       
			sub	ecx,edx
			push	ecx			; ecx = New line length
			call	FindLine		; Look for same line number in Basic text
			push	edx			; edx points on line found or line with upper number
			jnz	@F			; or on top of Basic text if nothing found
			; Same line number found, cancel existing line 
			mov	ecx,edx			; ecx = Top of line to cancel
			call	FindNextLine		; edx = end of line
			mov	ebx,TxtEnd		; ebx = End of Basic text
			call	MoveUp			; Cancel line by moving up following text
			mov	TxtEnd,ecx		; Update End of text pointer
			; Insert new line
@@:			pop	ecx			; ecx = where to insert line (push edx)
			mov	ebx,TxtEnd
			pop	eax			; eax = Number of new line chars.
			push	ebx
			cmp	al,5			; If al=5 (4 bytes line number+CR):Empty line, No insertion
			jz	Prompt
			add	ebx,eax			; ebx = new Basic text end
			cmp	ebx,LineNum-1		; Room enough in basic text memory ?
			jnc	@F			; No, error <Out of memory>
			mov	TxtEnd,ebx		; Yes, update end of text pointer
			pop	edx			; edx = old end of text pointer
			call	MoveDown		; Moves text following new line
			pop	edx			; Restore new line pointers in buffer
			pop	ebx
			call	MoveUp			; and insert new line.
			jmp	Prompt			; Wait for a new entry
@@:			mov	edi,offset Err4		; Error : <Out of memory>
			jmp	DispError

;-----------------------------------------------------------------------------------------------
; Direct Commands
;-----------------------------------------------------------------------------------------------

; Note: Commands processing is divided into 2 stages.
; stage 1: performs syntax checking on awaited arguments (not on real passed values)
;          encode retrieved expressions in expr buffer and put a pointer
;          into expr entry of command definition.
; stage 2: get the pointer and execute the encoded expressions in sequence
;	   (values coherencies are checked here).
;
; The "mov SaveCompExprPtr,0" line simply tells the precompiler to put a null pointer
; into expr entry (for command that can or must run without parameters).
;
; Direct commands have their very own precomp buffers for obvious reasons.
; Result and reason of precompilation is named SPEED.

;-----------------------------------------------------------------------------------------------
; NEW cancels text program in memory.
; In fact, modify Txtend pointer and reset the variables.
			align	8
CMD_NEW:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_NEWCOMP:		call	ReNewAll
			jmp	Ready			; Reset pointers before prompt
			align	8
ReNewAll:		invoke	FillMem,AZVar,(26*16),0
			invoke	FillMem,ArrayVar,(ARRAY_NBR*16),0
			mov	eax,TxtStart
			mov	TxtEnd,eax		; TxtEnd = start address of basic text area
			mov	eax,CompStart
			mov	CompEnd,eax
			mov	eax,LitStrings
			mov	LitStringsPtr,eax
			mov	SaveLineNumber,-1	; Reset saved line number here
			ret

;-----------------------------------------------------------------------------------------------
; RUN(CR) finds the first stored line, stores it's address and starts to execute it.
; Note that only TAB_CMD commands are legal for stored programs
;
; There are three more entries in 'RUN':
;       'NextLine' find the next line, store it's address and execute it
;       'RunCont' Continue the execution on same line
			align	8
CMD_RUN:		mov	SaveCompExprPtr,0
			mov	eax,LitStrings		; Reset strings here
			mov	LitStringsPtr,eax
			jmp	TestNoArguments
			align	8
CMD_RUNCOMP:		mov	ebx,CompStart
			push	ecx
			mov	ecx,CompExpr
			mov	PrecompDirect,0
			call	PreComp			; Pre-compile the program
			pop	ecx
			; Reset if stack here
			call	InitP			; Init. FOR..NEXT and GOSUB stacks pointers
			mov	edx,CompStart		; edx = first Basic text program address
NextLine:		xor	ebx,ebx			; Line number = 0
RunCont:		call	LineNumCompareComp	; End of Basic text ?
			jnc	ExecLine		; No, execute line
			call	CheckPendingIf
			mov	SaveLineNumber,-1
			jmp	Ready

;----------------------------------------
; 'CONT' continues a previous run command at current line
			align	8
CMD_CONT:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_CONTCOMP:		mov	ebx,SaveLineNumber
			cmp	ebx,-1			; No run yet ?
			je	CMD_RUNCOMP		; Run it from beginning
			mov	edx,CompStart
			jmp	RunCont			; Continue...

;-----------------------------------------------------------------------------------------------
; HELP prints available commands and functions.
			align	8
CMD_HELP:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_HELPCOMP:		lea	edx,HelpDcommands
			xor	al,al
			call	DispStr
			lea	eax,DCommandsHelp
			call	PrintHelpMessage
			lea	edx,HelpCommands
			xor	al,al
			call	DispStr
			lea	eax,CommandsHelp
			call	PrintHelpMessage
			lea	edx,HelpFunctions
			xor	al,al
			call	DispStr
			lea	eax,FunctionsHelp
			call	PrintHelpMessage
			jmp	Ready
PrintHelpMessage:	mov	edx,[eax]
			cmp	edx,-1			; End of table ?
			je	HelpMessageEnd
			test	edx,edx			; Carriage return ?
			jnz	NoHelpCrLf
			push	eax
			call	CrLf
			pop	eax
			add	eax,4
			jmp	PrintHelpMessage
			align	8
NoHelpCrLf:		push	eax
			mov	ah,8			; Pad keyword to 8 chars
			call	DispNumStr
			pop	eax
			add	eax,4
			jmp	PrintHelpMessage
			align	8
HelpMessageEnd:		ret

;-----------------------------------------------------------------------------------------------
; LIST has two forms : LIST(CR) lists all saved Basic lines
;                    : LIST Expr(CR) start list at given expression
;		       or next if not found
; You can pause listing with 'Space' key and stop it with 'ESC' key
			align	8
CMD_LIST:		call	TrimSpaces
			cmp	byte ptr [edx],":"
			je	@F
			cmp	byte ptr [edx],0dh
			je	@F
			jmp	EvalExpr		; Is there a line number ? If not, edit the first line nbr.
			align	8
@@:			mov	SaveCompExprPtr,0
			ret
			align	8
CMD_LISTCOMP:		xor	ebx,ebx
			cmp	dword ptr [edx],0	; No expression
			jne	GetListArgument
			add	edx,4
			jmp	@F
			align	8
GetListArgument:	push	edx
			mov	edx,[edx]
			call	EvalExprCOMP			
			pop	edx
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			add	edx,4
@@:			call	FindLine		; Find this line number or next line
LS1:			jnc	@F			; If c=1, end of text, else print next line
LS2:			mov	word ptr btemp1,0	; clean keyboard flags
			jmp	Ready
			align	8
@@:			mov	ch,5			; 5 leading spaces before line number
			call	DispBasicLine		; Print the line
			call	TestInpBuf		; ESC key pressed ?
			cmp	bl,27
			je	LS2
			cmp	bl,32			; SPACE key ?
			jne	LS4
LS3:			call	TestInpBuf		; Loop waiting a second Space to continue list
			cmp	bl,27			; or ESC to stop
			je	LS2
			cmp	bl,32
			jne	LS3
LS4:			mov	ebx,25			; 25 millisec. pause before printing next line
			call	WaitLoop
			xor	ebx,ebx
			call	LineNumCompare		; Find next line to print
			jmp	LS1			; and loop back

;-----------------------------------------------------------------------------------------------
; Edit a Basic text line and allow modifications with line editor
			align	8
CMD_EDIT:		call	TrimSpaces
			cmp	byte ptr [edx],":"
			je	@F
			cmp	byte ptr [edx],0dh
			je	@F
			jmp	EvalExpr		; Is there a line number mentioned ?
			align	8
@@:			mov	SaveCompExprPtr,0
			ret
			align	8
CMD_EDITCOMP:		xor	ebx,ebx
			cmp	dword ptr [edx],0	; No expression ?
			jne	GetEditArgument
			add	edx,4
			jmp	@F			; Find first available line
			align	8
GetEditArgument:	push	edx
			mov	edx,[edx]
			call	EvalExprCOMP			
			pop	edx
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			add	edx,4
@@:			call	FindLine		; Find specified line or first line
			push	edx			; Save line number
			jnc	@F			; Edit line
			pop	edx			; No line to edit, return to prompt
			jmp	Ready
			align	8
@@:			xor	ch,ch			; No leading spaces before line number
			call	DispBasicLine		; Print the line
			xor	eax,eax
			mov	ax,Csbi_CursorPositionX	; Set new cursor position at the beginning of edited line
			mov	bx,Csbi_CursorPositionY
			xor	edx,edx
			mov	dx,Csbi_MaximumWindowSizeX
			push	ebp			; ebp = Number of line chars.
@@:			dec	bx			; Previous Csbi_MaximumWindowSizeX chars
			sub	ebp,edx			; edx contains max number of chars/line
			jnc	@B
			mov	Csbi_CursorPositionX,ax
			mov	Csbi_CursorPositionY,bx
			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			call	TestApiError
			mov	edi,LineBuffer		; and print line
			inc	edi
			pop	ebp
			invoke	ReadConsoleOutputCharacter,hStdOut,edi,ebp,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError
			mov	LinePtr,1
			inc	ebp
			mov	LineLen,ebp
			jmp	EDMode			; then jump to the editor

;-----------------------------------------------------------------------------------------------
; LOAD loads a basic text file with '.MBI' extension;
; tests - if file fits in text memory
;         file signature to be sure this file was created with a MBasic 'SAVE' command
			align	8
CMD_LOAD:		call	TestArgumentsExpected
			call	SaveFileTitle
			mov	SaveCompExprPtr,0
			ret
			align	8
CMD_LOADCOMP:		call	AutoLoadFile
			jmp	Ready
			align	8
AutoLoadFile:		invoke	CreateFile,FileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
			cmp	eax,INVALID_HANDLE_VALUE	; File doesn't exist ?
			jne	@F				; No,continue
E20:			mov	edi,offset Err20		; Yes <File not found>
			jmp	DispError
			align	8
@@:			mov	FileHandle,eax
			invoke	GetFileSize,FileHandle,addr dtemp1
			call	TestApiError
			cmp	eax,MEMSIZE			; File length < Basic text memory available ?
			jb	@F				; Yes, continue
E21:			invoke	CloseHandle,FileHandle
			mov	edi,offset Err21		; No, error <File too large>
			jmp	DispError
			align	8
@@:			push	eax				; Save file length
			invoke	ReadFile,FileHandle,addr dtemp1,4,addr dtemp2,0
			call	TestApiError			; read header to be sure file comes from MBasic
			cmp	dtemp1,001485342h		; File signature
			je	@F
			invoke	CloseHandle,FileHandle
			mov	edi,offset Err22		; No match, error <Not a MBasic file>
			jmp	DispError
			align	8
@@:			pop	eax				; File length
			sub	eax,4				; minus header
			push	eax				; Save real file length
			invoke	ReadFile,FileHandle,dword ptr TxtStart,eax,addr dtemp1,0
			call	TestApiError			; File text is transfered in basic text area
			invoke	CloseHandle,FileHandle
			call	ReNewAll			; Clear all
			pop	eax				; Basic text length
			mov	ebx,TxtStart			; Update end of text pointer
			add	ebx,eax
			mov	TxtEnd,ebx
			ret

;-----------------------------------------------------------------------------------------------
; SAVE saves a MBasic text file with signature header
			align	8
CMD_SAVE:		call	TestArgumentsExpected
			call	SaveFileTitle
			mov	SaveCompExprPtr,0
			ret
			align	8
CMD_SAVECOMP:		mov	eax,TxtEnd		; Is there code to save ?
			sub	eax,TxtStart
			test	eax,eax
			jz	Ready			; No
			push	eax			; Yes, save code length
			invoke	CreateFile,FileName,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
			call	TestApiError		; Open file to write in
			mov	FileHandle,eax
			mov	dtemp1,001485342h	; Header to identify MBasic file
			; Write signature
			invoke	WriteFile,FileHandle,addr dtemp1,4,addr dtemp2,0
			call	TestApiError		; Write header
			pop	eax			; Code length to save
			invoke	WriteFile,FileHandle,dword ptr TxtStart,eax,addr dtemp2,0
			call	TestApiError		; Basic text area is transfered to disk
			invoke	CloseHandle,FileHandle
			jmp	Ready

;-----------------------------------------------------------------------------------------------
; Other Commands
;-----------------------------------------------------------------------------------------------

;-----------------------------------------------------------------------------------------------
; STOP is a simple jump to InputLoop, keeping program variables their values
; FOR..NEXT and GOSUB stacks are not modified
			align	8
CMD_STOP:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_STOPCOMP:		call	CanExecute
			jz	NoThenUnVerified
			mov	LineNumber,0
			jmp	Prompt

;-----------------------------------------------------------------------------------------------
; GOTO(EXPR) evaluates the expression, finds the target line and jumps to Execline to do it
			align	8
CMD_GOTO:		call	TestArgumentsExpected
			jmp	EvalExpr
			align	8
CMD_GOTOCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]		; Get expression address
			call	EvalExprCOMP		; Evaluate expression
			pop	edx
			add	edx,4
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			push	edx			; Save line number in case of error.
			call	FindLineComp		; Look for line expression
			jz	@F			; Found line number, execute it
			mov	edi,offset Err10	; else, error <Line number not found>
			jmp	DispError
			align	8
@@:			pop	eax			; Clear the stack
			jmp	ExecLine		; and execute target line

;-----------------------------------------------------------------------------------------------
; WINDOW(X,Y) or WINDOW (EDIT)
; With 2 parameters, WINDOW resizes current console window with (X) width, (Y) height,
; (EDIT) resizes console window with original size, position and colors of MBasic editor
; (See problems in DOC file)
			align	8
CMD_WINDOW:		call	TestArgumentsExpected
			mov	WindowCmdParms2Pos,edx
			mov	ebx,TBL_EDIT		; 'EDIT' keyword after 'WINDOW' ?
			jmp	SearchKeyword
			align	8
E37:			mov	edi,offset Err37	; Argument expected
			jmp	DispError
			align	8
WINDOW_EDIT:		mov	eax,CompExprPtr		; Write -1 to invalidate everything later
			mov	byte ptr [eax],-1
			inc	CompExprPtr
			ret
			align	8
WINDOW_XXX:		mov	edx,WindowCmdParms2Pos
			call	EvalExpr
			call	FindComma
			jnz	E17
			jmp	EvalExpr
			align	8
CMD_WINDOWCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]		; Get expression address
			cmp	byte ptr [edx],-1
			jne	WasWindowEdit
			call	WinEdit
			pop	edx
			add	edx,4
			jmp	NextCmd
			align	8
WasWindowEdit:		call	EvalExprCOMP		; Evaluate expression
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			test	ebx,ebx
			jle	E18			; Must be >0
			mov	si,bx			; Detected, si = window width
			push	esi
			call	EvalExprCOMP		; Check second parameter
			pop	esi
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			test	ebx,ebx
			jle	E18			; Must be >0
			mov	di,bx			; di = window height
			call	RedimWindow
			pop	edx
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; PRINT command is 'PRINT ....;' or 'PRINT ....(CR)'
; where '....' is a list of expressions, formats, backarrows and strings.
; these items are separated by semicolons.
;
; a format is a '#' sign followed by a number. It controls the number of spaces the value of an
; expression is to be printed. It stays effective for the rest of the print, unless changed by
; another format. No format specified, no padding added.
;
; A string is quoted in a pair of double quotes.
;
; A (CRLF) is generated after the entire list has been print.
			align	8
CMD_PRINT:		call	TestArgumentsExpected
GetPrintExpr:		cmp	byte ptr [edx],0dh
			je	EndOfPrint
			mov	ah,":"
			call	SkipSpaces
			jz	EndOfPrint
			mov	ah,'#'			; Format sign ?
			call	SkipSpaces
			jnz	@F			; No, test other possibilities
			invoke	AddWrapStringPadding
			call	EvalExpr		; Yes, evaluate padding expression
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E26
			jmp	CheckPrintEnd
			align	8
@@:			mov	ah,";"			; Is it a ';'
			call	SkipSpaces
			jnz	GetPrintElement
			jmp	GetPrintExpr
			align	8
GetPrintElement:	call	EvalExpr		; Yes, evaluate expression
CheckPrintEnd:		call	TrimSpaces
			cmp	byte ptr [edx],0dh
			je	EndOfPrint
			mov	ah,";"			; Must follow an expression if not EOL
			call	SkipSpaces
			jz	ProceedNExtPrintArg
			mov	ah,":"
			call	SkipSpaces
			jnz	E26
			jmp	EndOfPrint
			align	8
ProceedNExtPrintArg:	jmp	GetPrintExpr
EndOfPrint:		invoke	AddWrapExprEnd
			ret
			align	8
CMD_PRINTCOMP:		call	CanExecute
			jz	NoThenUnVerified
			xor	cl,cl
			push	edx
			mov	edx,[edx]		; Get expression address
GetNextPrintArg:	cmp	byte ptr [edx],WRAP_EXPREND
			je	PrintDone
			cmp	byte ptr [edx],WRAP_STRPADDING
			jne	AddStrPad
			inc	edx			; Pass padding byte
			push	ecx
			call	EvalExprCOMP
			pop	ecx
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			inc	ebx
			mov	ecx,ebx
			jmp	GetNextPrintArg
			align	8
AddStrPad:		push	ecx
			call	EvalExprCOMP
			pop	ecx
			cmp	ExprFinalResult,VAR_INTEGER
			jne	PrintInteger
			push	edx
			push	ecx
			call	DispNumber
			pop	ecx
			pop	edx
			jmp	GetNextPrintArg
			align	8
PrintInteger:		cmp	ExprFinalResult,VAR_FLOAT
			jne	PrintFloat
			push	edx
			push	ecx
			call	DispFloat
			pop	ecx
			pop	edx
			jmp	GetNextPrintArg
			align	8
PrintFloat:		cmp	ExprFinalResult,VAR_STRING
			jne	PrintString
			push	edx
			push	ecx
			mov	edx,ebx
			call	QuotedString
			pop	ecx
			pop	edx
PrintString:		jmp	GetNextPrintArg
			align	8
PrintDone:		pop	edx
			add	edx,4
			jmp	NextCmd			; Run next command

;----------------------------------------------------------------------------------------------
; GOSUB (EXPR): or GOSUB EXPR(CR) is like the 'GOTO' command except that the current text
; pointer and line number are saved so that execution can be continued after the subroutine
; 'RETURN'. In order that 'GOSUB' can be nested (and even recursive), the save area must be
; pileed. The stack pointer is saved in 'GosStackPtr'. Current GOSUB is not saved in the stack.
; It will be only if a second GOSUB appear before the first RETURN. A maximum of 16 GOSUB can
; be nested thus. If we are in the main routine, 'GosStackPtr' must be set to zero to avoid
; RETURN without GOSUB. This is done in the main init routine, before 'Ready'   
			align	8
CMD_GOSUB:		call	TestArgumentsExpected
			jmp	EvalExpr
			align	8
CMD_GOSUBCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]
			call	EvalExprCOMP
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			pop	edx
			add	edx,4
			push	edx				; Save text pointer
			call	FindLineComp			; Look for line number (ebx) in Basic text
			je	@F				; Line found : edx = top of line, ax = line number
			mov	edi,offset Err10		; line not found : error <Line number not found>
			jmp	DispError
			align	8
@@:			cmp	GosLineNum,0			; Other GOSUB processing ?
			je	GS1				; No, update GosLineNum & GosPtrTxt
			mov	ebx,GosStackPtr			; Yes, room enough in Gosub stack ?
			add	ebx,8
			cmp	ebx,TxtStart			; ebx < end of Gosub stack ?
			jbe	@F				;     Yes, save previous GOSUB parameters in stack
			mov	edi,offset Err11		;     No, Error <More than 16 GOSUB levels>
			jmp	DispError
			align	8
@@:			sub	ebx,4
			mov	edi,LineNumber
			mov	[ebx],edi			; Save GOSUB Line number
			sub	ebx,4
			mov	edi,GosPtrTxt
			mov	[ebx],edi			; Save Text pointer (1st. address after GOSUB command)
			add	GosStackPtr,8			; Update stack pointers (may be optimized)
GS1:			mov	ebx,LineNumber			; save current GOSUB Line Num. & Text Ptr.
			mov	GosLineNum,ebx
			pop	ebx				; ebx = current text pointer
			mov	GosPtrTxt,ebx			; save it
			mov	LineNumber,eax			; update new line number
			mov	SaveLineNumber,eax
			jmp	ExecLine			; and execute GOSUB target line found by FindLine

;----------------------------------------------------------------------------------------------
; RETURN(CR) undoes everything that 'GOSUB' did, and thus return the execution to the command
; after the most recent 'GOSUB'. If GosLineNum = 0, it indicates that corresponding 'GOSUB'
; dos'nt exist and generates an error message
			align	8
CMD_RETURN:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_RETURNCOMP:		call	CanExecute
			jz	NoThenUnVerified
			mov	ebx,GosLineNum			; Does a GOSUB exist ?
			test	ebx,ebx
			jnz	@F				; Yes, OK
			mov	edi,offset Err12		; No, error <RETURN without GOSUB>
			jmp	DispError
			align	8
@@:			mov	LineNumber,ebx			; Update corresponding GOSUB line number
			mov	SaveLineNumber,ebx
			mov	edx,GosPtrTxt			; and text pointer
			mov	ecx,GosStackPtr
			cmp	ecx,GosubStack			; Is there other GOSUB(S) in the stack
			jnz	@F				; Yes, tranfer the last one line number & text pointer
			mov	GosLineNum,0			; No, GosLineNum = 0 and resume execution after GOSUB
			jmp	RT1
			align	8
@@:			sub	ecx,4
			mov	edi,[ecx]
			mov	GosLineNum,edi			; Previous GOSUB line number from stack
			sub	ecx,4
			mov	edi,[ecx]
			mov	GosPtrTxt,edi			; and text pointer
			mov	GosStackPtr,ecx			; update stack pointer
RT1:			jmp	NextCmd				; Continue exécution after corresponding GOSUB

;----------------------------------------------------------------------------------------------
; FOR has two forms :
;   'FOR VAR=EXP1 TO EXP2 STEP EXP3'
;   'FOR VAR=EXP1 TO EXP2'
; The second form means the same as the first but EXP3 = 1 
; MBasic will find the variable address and set its value to the current value of EXP1.
; It also evaluates EXP2 and EXP3 and save all of these together with the text pointer in
; the ForNextStack area. If there is already something in the save area (this is indicated
; by a non-zero ForAdrVar), then the old save area is saved in the stack before the new one
; overwrite it.
; MBasic will then dig in the stack and find out if this same variable was used in 
; another currently active FOR loop. If that is the case, an error occur.
			align	8
CMD_FOR:		call	TestArgumentsExpected
			call	VarEquExpr		; Evaluate Variable=EXPR
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E44
			call	CheckLastSpace
			jne	ErrorTO
			mov	ebx,TBL_TO		; Execute keyword 'TO'
			jmp	SearchKeyword
			align	8
E44:			mov	edi,offset Err44	; Error <Numeric value expected>
			jmp	DispError
			align	8
TOKEYWORD:		call	EvalExpr		; Evaluate expression after 'TO' (come back here after execute)
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E44
			mov	al,[edx-1]
			mov	RequiredSpace,al
			mov	ebx,TBL_STEP		; Execute keyword STEP
			jmp	SearchKeyword
			align	8
STEPKEYWORD:		cmp	RequiredSpace," "
			jne	E26
			call	EvalExpr		; Evaluate STEP expression (come back here)
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E44
			jmp	@F
			align	8
NOSTEPKEYWORD:		invoke	AddWrapInteger,1	; STEP 1 (default)
@@:			mov	ebx,LineNumber		; Save current line
			invoke	AddWrapInteger,ebx
			invoke	AddWrapInteger,edx
			ret
			align	8
CMD_FORCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			call	LoadVariables		; Get variable (ecx is real var address)
			mov	edx,eax			; eax is next entry in expr table
			push	ecx
			call	EvalExprCOMP		; Limit
			push	ebx
			call	EvalExprCOMP		; Step
			pop	eax			; Here we got all the elements
			pop	ecx
			cmp	ForAdrVar,0		; Another FOR..NEXT active loop ?
			jz	@F			; No
			push	eax			; (trash eax)
			call	ForPushStack		; Yes, save parameters of this loop in stack
			pop	eax
@@:			; eax=limit
			; ebx=step
			; ecx=var address
			; Load the variables...
			mov	ForLimit,eax
			mov	ForIncrement,ebx
			mov	ForAdrVar,ecx
			mov	eax,LineNumber		; Get current line number
			mov	ForLine,eax
			; Check if FOR variable is already used in another active FOR..NEXT loop
			mov	edx,ForAdrVar		; edx = address of current FOR variable
			mov	ebx,ForStackPtr		; ebx = Stack pointer
			add	ebx,16			; Now points on possible previous active FOR..NEXT loop
FR4:			cmp	ebx,GosubStack		; out of stack limit ?
			jae	FR5			; Yes, no identical variable, continue
			cmp	edx,ebx			; No, compare previous variable with current one
			jne	@F			;    Identical ? 
			mov	edi,offset Err13	;    Yes, error <Duplicate FOR..NEXT variable>
			jmp	DispError
			align	8
@@:			add	ebx,20			;    No, loop to previous FOR..NEXT stack area
			jmp	FR4
			align	8
FR5:			pop	edx
			add	edx,4
			mov	eax,edx
			mov	ForPtrTxt,eax		; Should point on next line (error if not)
			jmp	NextCmd

;----------------------------------------------------------------------------------------------- 
; Jump here if keyword 'TO' isn't found in a FOR..TO loop
			align	8
ErrorTO:		mov	edi,offset Err5
			jmp	DispError

;----------------------------------------------------------------------------------------------- 
; NEXT can be followed by VAR or not. Either way, MBasic checks if a FOR loop is
; currently active then adds the STEP value to that variable and check the result with the limit.
; If it is within the LIMIT, control loops back to the command following the FOR. If outside the
; limit, the save area is purged (ForAdrVar=0) or replaced with superior level active FOR loop
; and execution continues after NEXT.
; in both cases, edx points on next command to execute.
			align	8
CMD_NEXT:		call	TestVar			; Test variable following NEXT
			jc	NoNextVar
			cmp	cl,WRAP_ARRAYADDRESS
			je	DoneNextVar
			cmp	cl,WRAP_VARADDRESS
			jne	E38
DoneNextVar:		ret
NoNextVar:		mov	SaveCompExprPtr,0
			ret
			align	8
CMD_NEXTCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]
			xor	ebx,ebx			; No var addr in argument is default
			test	edx,edx			; No variable specified previously ?
			jz	NoNextVarToGet
			call	EvalExprCOMP
NoNextVarToGet:		push	edx			; Save text pointer
			mov	edx,ebx			; edx = NEXT address variable
			mov	ebx,ForAdrVar		; ebx = FOR address variable
			test	ebx,ebx			; = 0 ?
			jnz	@F			; No, continue
			mov	edi,offset Err15	; Yes, no corresponding FOR, Error <NEXT without FOR>
			jmp	DispError
			align	8
@@:			test	edx,edx			; found NEXT variable ?
			jz	@F			; No, skip variables comparison 
			cmp	edx,ebx			; Compare NEXT adr. var. (edx) with FOR adr. var. (ebx)
			jz	@F			; OK, identical
			mov	edi,offset Err16	; Else, Error <NEXT variable doesn't match corresponding FOR>
			jmp	DispError
			align	8
@@:			mov	edx,[ebx]		; edx = FOR variable value
			mov	edi,ForIncrement
			add	edi,edx			; Add increment (1 or STEP value)
			mov	[ebx],edi		; save it
			mov	edx,ForLimit		; edx = FOR limit value (TO ...)
			cmp	edx,edi			; FOR value > limit ?
			pop	edx			; Restore text pointer (following NEXT)
			jc	@F			; Yes, end of FOR loop
			mov	ebx,ForLine		; No, restore FOR line number
			mov	LineNumber,ebx
			mov	SaveLineNumber,ebx
			pop	edx
			mov	edx,ForPtrTxt		; and text pointer
			jmp	NextCmd			; and continue execution after FOR command
			; Update FOR..NEXT stack and pointer if other active loops.
			align	8
@@:			mov	ForAdrVar,0		; Keep 0 value if no other loop to pop
			mov	eax,ForStackPtr
			cmp	eax,GosubStack		; Is there any FOR..NEXT loop to pop ?
			jae	@F			; No, no other FOR..NEXT active loop
			xchg	eax,esp			; Yes, Use esp to restore previous loop parameters
			pop	ForIncrement
			pop	ForLimit
			pop	ForLine
			pop	ForPtrTxt
			pop	ForAdrVar
			xchg	eax,esp			; restore esp
			add	ForStackPtr,20		; ForStackPtr now points on previous FOR..NEXT stack
@@:			pop	edx
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; REM command doesn't exist. (') char. is used in place of REM 
; Use a part of IF Cmd. (false condition) to skip rest of current line and execute next line
			align	8
CMD_REM:		mov	SaveCompExprPtr,0
			ret
CMD_REMCOMP:		add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; IF command can evaluate following expressions in two ways.
;
; Mode 1:
;
; If condition is verified (=1), execute rest of current line.
; If condition is unverified (=0), skip the rest of line and execute next line. 
;
; Mode 2:
;
; THEN keyword is found after the IF expression.
; If the expression is verified (=1):
; Check if this is the last keyword:
;  If no then acts as in Mode 1 (execute rest of current line).
;  If yes enter IF mode
; If the expression is unverified (=0):
;  Enter IF mode 
; The IF mode:
;  Pass over all commands but check stacks for IF/ELSE/ENDIF commands
;  to find corresponding ELSE or ENDIF command.
			align	8
GhostedIF:		; Push ghosted condition (-1) (for ELSE)
			; To keep stack balance (for ENDIFs matching)
			push	edx
			mov	edx,[edx]
			cmp	dword ptr [edx+1],1			
			je	GhostNoThen
			; -1 will ensure that the following ELSE commands belonging
			; to a ghosted IF, won't modify anything.
			invoke	PushIfStack,-1
			pop	edx
			add	edx,4
			jmp	NoThenUnVerifiedEnd
			; Then keyword not found
			align	8
GhostNoThen:		pop	edx
			add	edx,4
			jmp	NoThenUnVerifiedEnd
			align	8
CMD_IF:			push	CompExprPtr
			invoke	AddWrapInteger,0
			call	EvalExpr			; Store expression
			mov	al,[edx-1]
			mov	RequiredSpace,al
			mov	ebx,TBL_THENPRECOMP		; Search keyword THEN
			jmp	SearchKeyword
			align	8
PreCompThenFound:	
PreCompThenNotFound:	cmp	RequiredSpace," "
			jne	E26
			; Reduce IF possibilities to 2 proposals:
			; "single line" or "multi lines"
			call	TrimSpaces
			cmp	byte ptr [edx],0dh
			je	@F
			cmp	byte ptr [edx],"'"
			je	@F
			cmp	byte ptr [edx],":"
			je	@F
			; Single line IF
			pop	eax				; Put if type in first position
			mov	dword ptr [eax+1],1		; (this will speedup ghostIFs)
			ret
			align	8
@@:			; Multi lines IF
			pop	eax
			mov	dword ptr [eax+1],0
			ret
			align	8
CMD_IFCOMP:		call	CanExecute
			jz	GhostedIF
			push	edx
			mov	edx,[edx]
			push	dword ptr [edx+1]
			add	edx,5
			call	EvalExprCOMP			; Evaluate if expression
			cmp	ExprFinalResult,VAR_FLOAT
			jne	IfFloat
			ftst					; Compare fp with 0.0
			fstsw	ax
			fstp	FltGarb				; Garbage it now
			and	eax,100010100000000b
			cmp	eax,100000000000000b
			je	IFUnVer
			jmp	IFVer
IfFloat:		test	ebx,ebx				; Boolean result of expression = 0 (FALSE) ?
			jz	IFUnVer				; Yes, skip end of line & execute next line
IFVer:			pop	eax
			test	eax,eax
			jnz	WasMultiIFVer
			invoke	PushIfStack,1			; Push entry into the stack
WasMultiIFVer:		pop	edx
			add	edx,4
			jmp	NextCmd				; Continue normally
			align	8
IFUnVer:		pop	eax
			test	eax,eax
			jnz	WasMultiIFUnVer
			invoke	PushIfStack,0			; Push entry into the stack
WasMultiIFUnVer:	pop	edx
			add	edx,4
			jmp	NoThenUnVerifiedEnd
; Find true next line and execute it (usually next*2 one)
			align	8
NoThenUnVerifiedEnd:	mov	ebx,LineNumberComp
			inc	ebx
			call	FindNextLineCompEnd
			jc	@F
			jmp	ExecLine
			align	8
NoThenUnVerified:	mov	ebx,LineNumberComp
			inc	ebx
			call	FindEndOfLineComp
			jc	@F				; IF end of Basic text, jump to Prompt
			jmp	ExecLine			; else execute next line
			align	8
@@:			call	CheckPendingIf
			jmp	Ready

;-----------------------------------------------------------------------------------------------
; ENDIF simply pop the current IF stack level
; (Possible underflow is checked in the PopIfStack routine itself)
			align	8
CMD_ENDIF:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_ENDIFCOMP:		call	PopIfStack
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; ELSE retrieve current If stack value
; and invert it if it is not a ghosted one.
			align	8
CMD_ELSE:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_ELSECOMP:		call	PeekIfStack
			jns	InvertIf
			add	edx,4
			jmp	NextCmd				; Continue...
			align	8
InvertIf:		xor	eax,1				; Invert bit 0
			invoke	PokeIfStack,eax			; Save it
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; INPUT waits for a 32 bits signed value entry (with [positive] or negative sign).
; If an error occurs, input screen area is cleared and user have to type a new value.
; The variable following INPUT command. is then set to the value entered.   
; The execution will not terminate unless user type a valid value or 'ESC' 
			align	8
CMD_INPUT:		call	TestArgumentsExpected
			push	edx
			call	EvalExpr			; Get both arguments
			cmp	ExprFinalResult,VAR_STRING	; Can be a string
			pop	eax
			je	GetOptionalInputString
			mov	edx,eax				; Restore text position
			jmp	NoOptionalInputString
			align	8
GetOptionalInputString:	call	FindComma
			jnz	E17
NoOptionalInputString:	call	TestVar				; Test variable following INUT
			jc	E19
			cmp	cl,WRAP_ARRAY
			je	E38
			cmp	cl,WRAP_VAR
			je	E38
			ret
			align	8
CMD_INPUTCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]		; Get expression address
			call	EvalExprCOMP
			cmp	ExprFinalResult,VAR_STRING
			jne	@F
			push	edx
			push	ecx
			mov	edx,ebx
			call	QuotedString		; Print the prompt
			pop	ecx
			pop	edx
			cmp	byte ptr [edx],WRAP_EXPREND
			je	E37
			; Now get the variable
			call	EvalExprCOMP
@@:			push	ebx				; save variable address
			push	edx				; Save Basic text pointer
IP1:			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			call	TestApiError
			push	dword ptr Csbi_CursorPositionX	; Save cursor position X,Y in case of user input error
			mov	LineLen,1			; Inits for LineInput
			mov	LinePtr,1
			mov	LineMaxLen,18			; Line input length limited for fp
			call	LineInput			; Get an answer from user
			mov	LineMaxLen,255			; restore length of basic text line
			mov	edx,LineBuffer			; edx = start of expression entered by user
			inc	edx
			mov	wtemp3,0			; Flag for negative value (0 = +, 1 = -)
			push	ebx
			call	CheckTermSign
			test	ebx,ebx
			jz	@F
			mov	wtemp3,1
@@:			pop	ebx
			push	edx
			mov	al,[edx]
			call	TstNumFloat			; Input value in ebx
			cmp	WasFloat,VAR_FLOAT
			je	InputFloat
			cmp	WasFloat,VAR_INTEGER
			jne	@F
			mov	al,ch				; Number of digits
			test	al,al
			jnz	InputFloat
@@:			pop	edx
			call	GetPlainString
			test	ebx,ebx				; Error in string...
			jz	RestartInput
			jmp	InputString
			align	8
InputFloat:		add	esp,4				; Garbage
InputString:		mov	ah,0dh				; end with CR ?
			call	SkipSpaces
			jz	@F				; Yes, continue
RestartInput:		pop	ecx
			; No, restore input cursor position
			mov	dword ptr Csbi_CursorPositionX,ecx
			push	ecx
			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			mov	ebx,offset ClearInput-1		; Clear input screen area
			add	ebx,LineLen
			mov	byte ptr [ebx],0
			push	ebx
			mov	edx,offset ClearInput
			xor	al,al
			call	DispStr
			call	Beeep				; Error : Beep
			pop	ebx
			pop	ecx
			mov	byte ptr [ebx]," "
			; Restore again input cursor position
			mov	dword ptr Csbi_CursorPositionX,ecx
			jmp	IP1				; and user try again
			align	8
@@:			pop	edx				; Cursor position to garbage
			pop	edx				; Restore basic text pointer
			pop	eax				; Restore variable address
			cmp	WasFloat,VAR_STRING
			je	StoreStringFromInput
			cmp	WasFloat,VAR_INTEGER
			je	StoreIntegerFromInput
			push	dword ptr FltVal		; Store floating point value
			pop	dword ptr [eax]
			push	dword ptr FltVal+4
			pop	dword ptr [eax+4]
			mov	dword ptr [eax+8],VAR_FLOAT
			pop	edx
			add	edx,4
			jmp	NextCmd
			align	8
StoreIntegerFromInput:	cmp	wtemp3,0			; If negative value, Change sign
			je	@F
			call	ChangeSign
@@:			mov	[eax],ebx			; Store value in variable slot
			mov	dword ptr [eax+8],VAR_INTEGER
			pop	edx
			add	edx,4
			jmp	NextCmd
			align	8
StoreStringFromInput:	mov	[eax],ebx			; Store address in variable slot
			mov	dword ptr [eax+8],VAR_STRING
			pop	edx
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; LET is just a variable assignation wrapper
			align	8
CMD_LET:		mov	SaveCompExprPtr,0
			ret
			align	8
CMD_LETCOMP:		call	LoadVariables
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; GET returns ASCII key code typed or 0 in ebx
; A key must be typed to continue program execution (See unresolved problems in DOC.TXT) 
			align	8
CMD_GET:		call	TestVar			; Test variable following GET
			jc	E19
			cmp	cl,WRAP_ARRAY
			je	E38
			cmp	cl,WRAP_VAR
			je	E38
			ret
			align	8
E19:			mov	edi,offset Err19
			jmp	DispError
			align	8
E38:			mov	edi,offset Err38
			jmp	DispError
			align	8
CMD_GETCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]
			call	EvalExprCOMP
			mov	edi,ebx				; save ebx
			call	TestKeyb			; If key pressed, bl = key ASCII code,
			test	bl,bl				; else ebx = 0 (except for functions keys)
			jz	@F				; If bl = 0 or bl = 224 (cursor keys), check scan code
			cmp	bl,224
			je	@F
			cmp	bl,27				; 'ESC' allows user to leave infinite loop
			je	Ready
			jmp	GT1				; If none of above cases
			align	8
@@:			invoke	FlushConsoleInputBuffer,hStdIn	; Clear keyboard input buffer
			xor	ebx,ebx
			cmp	byte ptr EventBuffer+4,0	; Is a keypress ?
			je	GT1				; Yes, save it in bl
			mov	dl,byte ptr [EventBuffer+12]	; Read buffer keyboard scan code
			cmp	dl,59				; scan codes between 59 and 88 (function & cursor keys)
			jb	GT1				; becomes 225 to 254
			cmp	dl,88
			ja	GT1
			add	dl,166
@@:			mov	bl,dl
GT1:			mov	[edi],ebx			; result is placed in variable address
			mov	dword ptr [edi+8],VAR_INTEGER	; integer type
			pop	edx
			add	edx,4
			jmp	NextCmd
			
;-----------------------------------------------------------------------------------------------
; POKE X,Y[,Y,..] puts one or more 8 bits value (Y) in a memory location (X) inside Basic text
; area. Y may be repeated, separated by commas, for successive memory locations
			align	8
CMD_POKE:		call	TestArgumentsExpected
			call	EvalExpr		; Address to put datas on
			call	FindComma		; First data is not optional
			jnz	E17
GetPokeDatas:		call	EvalExpr
			call	FindComma		; Other datas are optional
			jnz	EndPokeDatas
			jmp	GetPokeDatas
			align	8
EndPokeDatas:		invoke	AddWrapExprEnd		; Mark expression as finished
			ret
			align	8
CMD_POKECOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]
			call	EvalExprCOMP			; Get address
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			push	ebx
@@:			call	EvalExprCOMP			; Get data
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			cmp	ebx,255
			ja	E2				; Yes, error <Numeric value overflow>
			mov	al,bl
			pop	ebx
			cmp	ebx,MEMDATSIZE			; Inside Basic text memory ?
			jae	E41				; No, error <Datas memory overflow>
			mov	ecx,ebx
			add	ecx,DatStart
			mov	[ecx],al			; Put byte
			inc	ebx				; next address
			push	ebx
			cmp	byte ptr [edx],WRAP_EXPREND	; No third argument ?
			je	PK0
			jmp	@B
			align	8
PK0:			pop	ebx				; Flush stack
			pop	edx
			add	edx,4
			jmp	NextCmd
			align	8
E41:			mov	edi,offset Err41		; Error: <Datas memory overflow>
			jmp	DispError

;-----------------------------------------------------------------------------------------------
; LOCATE X,Y[,Z] sets cursor position at column X, line Y. Upper left position is 0,0
; The new cursor position must be within the boundaries of the console screen.  
; Optional argument Z hides cursor if = 0 or makes cursor visible
			align	8
CMD_LOCATE:		call	TestArgumentsExpected
			call	EvalExpr
			call	FindComma
			jnz	E17
			call	EvalExpr
			call	FindComma
			jnz	PutOptionalLocate
			jmp	EvalExpr
			align	8
PutOptionalLocate:	invoke	AddWrapExprEnd		; Mark expression as finished
			ret
			align	8
E17:			mov	edi,offset Err17	; Error: <',' statement expected!>
			jmp	DispError
			align	8
CMD_LOCATECOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]			; Get expression address
			call	EvalExprCOMP
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			cmp	bx,Csbi_MaximumWindowSizeX	; X < Window Size X ?
			jc	@F				; Yes, continue
E18:			mov	edi,offset Err18		; No, error <Invalid argument>
			jmp	DispError
			align	8
@@:			mov	si,bx				; Save column in si
			call	EvalExprCOMP
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			cmp	bx,Csbi_MaximumWindowSizeY	; y < Window size Y ?
			jnc	E18
			mov	di,bx				; save line in di
			cmp	byte ptr [edx],WRAP_EXPREND	; No third argument ?
			je	LC0
			call	EvalExprCOMP
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			push	edx
			; Set cursor visible flag
			test	ebx,ebx				; (EXPR) = 0 ?
			jz	@F				; Yes, hide cursor
			mov	ebx,1				; No, set cursor
@@:			mov	CursorShape.bVisible,ebx
			call	SetConCursor
			call	TestApiError
			pop	edx
LC0:			mov	Csbi_CursorPositionX,si		; Update new cursor position
			mov	Csbi_CursorPositionY,di
			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			call	TestApiError
			pop	edx
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; COLOR X,[Y] set foreground (X) and background (Y) colors to be used with further PRINT commands
; Parameters values must be between 0 & 15 (DOS colors)
; IF Y is not mentioned background color is not changed.
			align	8
CMD_COLOR:		call	TestArgumentsExpected
			call	EvalExpr		; Get both arguments
			call	FindComma
			jnz	PutOptionalColor
			jmp	EvalExpr
PutOptionalColor:	invoke	AddWrapExprEnd		; Mark expression as finished
			ret		
			align	8
CMD_COLORCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]		; Get expression address
			call	EvalExprCOMP		; Get first color
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			cmp	ebx,16			; Foreground color > 15 ?
			jnc	E18
			push	ebx
			; Check if Y value was not present in expression
			mov	eax,ColorsCurrent
			and	eax,0f0h
			cmp	byte ptr [edx],WRAP_EXPREND
			je	RestoreBackGroundCol
			call	EvalExprCOMP		; Check second parameter
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			cmp	ebx,16			; Background color > 15 ?
			jnc	E18
			mov	eax,ebx			; Background			
			shl	al,4			; Background color in 4 left bits of al
RestoreBackGroundCol:	pop	ebx			; Foreground
			add	al,bl			; Foreground color in 4 right bits  
			mov	ColorsCurrent,eax	; Update current color
			pop	edx
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; TRON switch the escape key checking on
			align	8
CMD_TRON:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_TRONCOMP:		call	CanExecute
			jz	NoThenUnVerified
			mov	TrState,0
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; TROFF switch the escape key checking off
			align	8
CMD_TROFF:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_TROFFCOMP:		call	CanExecute
			jz	NoThenUnVerified
			mov	TrState,1
			add	edx,4
			jmp	NextCmd
	
;-----------------------------------------------------------------------------------------------
; CLS fills screen with spaces and colors attributes
			align	8
CMD_CLS:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
CMD_CLSCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			call	WE1
			pop	edx
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; WAIT(EXPR) waits for (EXPR) milliseconds then continue execution 
			align	8
CMD_WAIT:		call	TestArgumentsExpected
			jmp	EvalExpr		; Evaluate expression
			align	8
CMD_WAITCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			mov	edx,[edx]
			call	EvalExprCOMP			
			pop	edx
			add	edx,4
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			call	WaitLoop		; and wait for ebx milliseconds
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; Emits a 'beep' in internal loudspeaker or a standard Window alert sound 
; if a sound card connected.
			align	8
CMD_BEEP:		mov	SaveCompExprPtr,0
			jmp	TestNoArguments
			align	8
E42:			mov	edi,offset Err42	; Else, error <Nào argument allowed>
			jmp	DispError
			align	8
CMD_BEEPCOMP:		call	CanExecute
			jz	NoThenUnVerified
			push	edx
			call	Beeep
			pop	edx
			add	edx,4
			jmp	NextCmd

;-----------------------------------------------------------------------------------------------
; Functions
;-----------------------------------------------------------------------------------------------
			align	8
CompileFunction1Arg:	invoke	AddWrapFnc,eax
			call	TestParenth			; Evaluate (EXPR)
			jmp	FinalizeFnc
			align	8
CompileFunction2Arg:	invoke	AddWrapFnc,eax
			call	TestParenthOpen
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E26
			call	FindComma
			jnz	E17
			call	EvalExpr
			call	FindCloseBracket
			jnz	E30
			invoke	AddWrapCloseBracket
			jmp	FinalizeFnc
			align	8
CompileFunction3Arg:	invoke	AddWrapFnc,eax
			call	TestParenthOpen
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E26
			call	FindComma
			jnz	E17
			call	EvalExpr
			call	FindComma
			jnz	E17
			call	EvalExpr
			call	FindCloseBracket
			jnz	E30
			invoke	AddWrapCloseBracket
			jmp	FinalizeFnc
			align	8
CompileFunctionInt:	invoke	AddWrapFnc,eax
			invoke	PushOperStack,VAR_INTEGER
			jmp	FinalizeFnc
			align	8
CompileFunctionFlt:	invoke	AddWrapFnc,eax
			invoke	PushOperStack,VAR_FLOAT
			jmp	FinalizeFnc

;-----------------------------------------------------------------------------------------------
; RND(expr) = (cycle_counter*dl MOD EXPR)+1
			align	8
FNC_RND:		mov	eax,WRAP_FNC_RND
			jmp	CompileFunction1Arg
			align	8
FNC_RNDCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			cmp	ExprFinalResult,VAR_FLOAT
			jne	RndFloat
			call	TestGtZero
			push	edx
			fstp	FltGarb				; Get argument
			rdtsc
			and	edx,0ffh
			mul	edx
			and	eax,07fffffffh
			mov	FltFromInt,eax
			fild	FltFromInt
			fstp	FltGarb2
			fld	FltGarb
			fld	FltGarb2
			fprem
			fstp	FltGarb2			; Get st(0)
			fstp	FltGarb				; Flush st(1)
			fld	FltGarb2
			call	CheckSignFlt			; Ensure positive result
			pop	edx
			jmp	FinalizeFncCOMP
			align	8
RndFloat:		test	ebx,ebx
			js	E18				; Must be positive
			jz	E18				; And <> zero
			push	ecx
			push	edx
			rdtsc					; Read Pentium cycles counter
			and	edx,0ffh			; (!!! Only Pentium instruction !!!)
			mul	edx				; 0<=eax<=32700
			xchg	edx,ebx
			mov	ebx,eax
			call	Divide				; RND(N)=MOD(M,N)+1
			pop	edx
			pop	ecx
			call	CheckSign			; Ensure positive result
			inc	ebx
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; ABS(EXPR) returns absolute value of EXPR
			align	8
FNC_ABS:		mov	eax,WRAP_FNC_ABS
			jmp	CompileFunction1Arg
			align	8
FNC_ABSCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			cmp	ExprFinalResult,VAR_FLOAT
			jne	AbsFloat
			call	CheckSignFlt			; (result in st(0)
			jmp	FinalizeFncCOMP
			align	8
AbsFloat:		call	CheckSign			; Invert sign if negative
			or	eax,ebx
			jns	FinalizeFncCOMP			; OK, ret
			jmp	E18				; else error : <Invalid argument>

;-----------------------------------------------------------------------------------------------
; SGN(EXPR) returns the sign of EXPR
			align	8
FNC_SGN:		invoke	PokeOperStack,VAR_INTEGER
			mov	eax,WRAP_FNC_SGN
			jmp	CompileFunction1Arg
			align	8
FNC_SGNCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			; SGN returns an integer
			invoke	PokeOperStack,VAR_INTEGER
			cmp	ExprFinalResult,VAR_FLOAT
			jne	SgnFloat
			push	eax
			ftst
			fstsw	ax
			mov	bx,ax
			fstp	FltGarb				; (garbage)
			pop	eax
			and	bx,100010100000000b
			jz	Sgn_Return1
			cmp	bx,000000100000000b
			je	Sgn_ReturnM1
			cmp	bx,100000000000000b
			je	Sgn_Return0
			jmp	E24				; Shouldn't occurs
			align	8
SgnFloat:		test	ebx,ebx				; Test s flag
			jz	Sgn_Return0
			jns	Sgn_Return1
Sgn_ReturnM1:		or	ebx,-1				; Return -1
			jmp	FinalizeFncCOMP
			align	8
Sgn_Return0:		xor	ebx,ebx				; Return 0
			jmp	FinalizeFncCOMP
			align	8
Sgn_Return1:		mov	ebx,1				; Return 1
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; SIN(EXPR) returns the sine of EXPR
			align	8
FNC_SIN:		mov	eax,WRAP_FNC_SIN
			jmp	CompileFunction1Arg
FNC_SINCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fsin
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; COS(EXPR) returns the cosine of EXPR
			align	8
FNC_COS:		mov	eax,WRAP_FNC_COS
			jmp	CompileFunction1Arg
FNC_COSCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fcos
			jmp	FinalizeFnc

;-----------------------------------------------------------------------------------------------
; SEC(EXPR) returns the secant of EXPR
			align	8
FNC_SEC:		mov	eax,WRAP_FNC_SEC
			jmp	CompileFunction1Arg
FNC_SECCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fcos
			call	TestZero
			fstp	FltGarb
			fld1
			fdiv	FltGarb
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; ASIN(EXPR) returns the arc sine of EXPR
			align	8
FNC_ASIN:		mov	eax,WRAP_FNC_ASIN
			jmp	CompileFunction1Arg
FNC_ASINCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			fchs
			fmul	FltGarb
			fadd	Flt1
			call	TestGtZero
			fsqrt
			call	TestZero
			fstp	FltGarb2
			fld	FltGarb
			fdiv	FltGarb2
			fld1
			fpatan
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; ACOS(EXPR) returns the arc cosine of EXPR
			align	8
FNC_ACOS:		mov	eax,WRAP_FNC_ACOS
			jmp	CompileFunction1Arg
FNC_ACOSCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			fchs
			fmul	FltGarb
			fadd	Flt1
			call	TestGtZero
			fsqrt
			call	TestZero
			fstp	FltGarb2
			fld	FltGarb
			fchs
			fdiv	FltGarb2
			fld1
			fpatan
			fstp	FltGarb2
			fld1
			fld1
			fpatan
			fmul	Flt2
			fstp	FltGarb
			fld	FltGarb2
			fadd	FltGarb
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; ASEC(EXPR) returns the arc secant of EXPR
			align	8
FNC_ASEC:		mov	eax,WRAP_FNC_ASEC
			jmp	CompileFunction1Arg
FNC_ASECCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			fmul	FltGarb
			fsub	Flt1
			call	TestGtZero
			fsqrt
			call	TestZero
			fstp	FltGarb2
			fld	FltGarb
			fdiv	FltGarb2
			fld1
			fpatan
			fstp	FltGarb2
			fld1
			fld1
			fpatan
			fmul	Flt2
			fstp	FltGarb3
			fld	FltGarb
			fsub	Flt1
			call	SgnFlt
			fmul	FltGarb3
			fstp	FltGarb3
			fld	FltGarb2
			fadd	FltGarb3
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; ACOSEC(EXPR) returns the arc cosecant of EXPR
			align	8
FNC_ACOSEC:		mov	eax,WRAP_FNC_ACOSEC
			jmp	CompileFunction1Arg
FNC_ACOSECCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			fmul	FltGarb
			fsub	Flt1
			call	TestGtZero
			fsqrt
			call	TestZero
			fstp	FltGarb2
			fld	FltGarb
			fdiv	FltGarb2
			fld1
			fpatan
			fstp	FltGarb2
			fld1
			fld1
			fpatan
			fmul	Flt2
			fstp	FltGarb3
			fld	FltGarb
			call	SgnFlt
			fsub	Flt1
			fmul	FltGarb3
			fstp	FltGarb3
			fld	FltGarb2
			fadd	FltGarb3
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; ACOTAN(EXPR) returns the arc cotangent of EXPR
			align	8
FNC_ACOTAN:		mov	eax,WRAP_FNC_ACOTAN
			jmp	CompileFunction1Arg
FNC_ACOTANCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fld1
			fpatan
			fstp	FltGarb
			fld1
			fld1
			fpatan
			fmul	Flt2
			fstp	FltGarb2
			fld	FltGarb
			fadd	FltGarb2
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; COSEC(EXPR) returns the cosecant of EXPR
			align	8
FNC_COSEC:		mov	eax,WRAP_FNC_COSEC
			jmp	CompileFunction1Arg
FNC_COSECCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fsin
			call	TestZero
			fstp	FltGarb
			fld1
			fdiv	FltGarb
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; COTAN(EXPR) returns the cotangent of EXPR
			align	8
FNC_COTAN:		mov	eax,WRAP_FNC_COTAN
			jmp	CompileFunction1Arg
FNC_COTANCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fptan
			fstp	FltGarb
			call	TestZero
			fstp	FltGarb
			fld1
			fdiv	FltGarb
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; TAN(EXPR) returns the tangent of EXPR
			align	8
FNC_TAN:		mov	eax,WRAP_FNC_TAN
			jmp	CompileFunction1Arg
FNC_TANCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fptan
			fstp	FltGarb
			jmp	FinalizeFncCOMP
			
;-----------------------------------------------------------------------------------------------
; SQRT(EXPR) returns the square root of EXPR
			align	8
FNC_SQRT:		mov	eax,WRAP_FNC_SQRT
			jmp	CompileFunction1Arg
FNC_SQRTCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			ftst
			fstsw	ax
			and	eax,000000100000000b
			jnz	E18
			fsqrt
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; LOG(EXPR) returns the natural logarithm of EXPR
			align	8
FNC_LOG:		mov	eax,WRAP_FNC_LOG
			jmp	CompileFunction1Arg
FNC_LOGCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			call	DoLog
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; ATN(EXPR) returns the arc tangent of EXPR
			align	8
FNC_ATN:		mov	eax,WRAP_FNC_ATN
			jmp	CompileFunction1Arg
FNC_ATNCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fld1
			fpatan
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; EXP(EXPR) returns the e^EXPR
			align	8
FNC_EXP:		mov	eax,WRAP_FNC_EXP
			jmp	CompileFunction1Arg
FNC_EXPCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			call	DoExp
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HSIN(EXPR) returns the hyperbolic sine of EXPR
			align	8
FNC_HSIN:		mov	eax,WRAP_FNC_HSIN
			jmp	CompileFunction1Arg
FNC_HSINCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			call	DoExp
			fstp	FltGarb2
			fld	FltGarb
			fchs
			call	DoExp
			fstp	FltGarb3
			fld	FltGarb2
			fsub	FltGarb3
			fdiv	Flt2
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HCOS(EXPR) returns the hyperbolic cosine of EXPR
			align	8
FNC_HCOS:		mov	eax,WRAP_FNC_HCOS
			jmp	CompileFunction1Arg
FNC_HCOSCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			call	DoExp
			fstp	FltGarb2
			fld	FltGarb
			fchs
			call	DoExp
			fstp	FltGarb3
			fld	FltGarb2
			fadd	FltGarb3
			fdiv	Flt2
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HTAN(EXPR) returns the hyperbolic tangent of EXPR
			align	8
FNC_HTAN:		mov	eax,WRAP_FNC_HTAN
			jmp	CompileFunction1Arg
FNC_HTANCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			call	DoExp
			fstp	FltGarb2
			fld	FltGarb
			fchs
			call	DoExp
			fstp	FltGarb3
			fld	FltGarb2
			fadd	FltGarb3
			call	TestZero
			fstp	FltGarb4
			fld	FltGarb
			call	DoExp
			fstp	FltGarb2
			fld	FltGarb
			fchs
			call	DoExp
			fstp	FltGarb3
			fld	FltGarb2
			fsub	FltGarb3
			fdiv	FltGarb4
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HSEC(EXPR) returns the hyperbolic secant of EXPR
			align	8
FNC_HSEC:		mov	eax,WRAP_FNC_HSEC
			jmp	CompileFunction1Arg
FNC_HSECCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			call	DoExp
			fstp	FltGarb2
			fld	FltGarb
			fchs
			call	DoExp
			fstp	FltGarb3
			fld	FltGarb2
			fadd	FltGarb3
			call	TestZero
			fstp	FltGarb3
			fld	Flt2
			fdiv	FltGarb3
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HCOSEC(EXPR) returns the hyperbolic cosecant of EXPR
			align	8
FNC_HCOSEC:		mov	eax,WRAP_FNC_HCOSEC
			jmp	CompileFunction1Arg
FNC_HCOSECCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			call	DoExp
			fstp	FltGarb2
			fld	FltGarb
			fchs
			call	DoExp
			fstp	FltGarb3
			fld	FltGarb2
			fsub	FltGarb3
			call	TestZero
			fstp	FltGarb3
			fld	Flt2
			fdiv	FltGarb3
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HCOTAN(EXPR) returns the hyperbolic cotangent of EXPR
			align	8
FNC_HCOTAN:		mov	eax,WRAP_FNC_HCOTAN
			jmp	CompileFunction1Arg
FNC_HCOTANCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			call	DoExp
			fstp	FltGarb2
			fld	FltGarb
			fchs
			call	DoExp
			fstp	FltGarb3
			fld	FltGarb2
			fsub	FltGarb3
			call	TestZero
			fstp	FltGarb4
			fld	FltGarb
			call	DoExp
			fstp	FltGarb2
			fld	FltGarb
			fchs
			call	DoExp
			fstp	FltGarb3
			fld	FltGarb2
			fadd	FltGarb3
			fdiv	FltGarb4
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HASIN(EXPR) returns the inverted hyperbolic arc sine of EXPR
			align	8
FNC_HASIN:		mov	eax,WRAP_FNC_HASIN
			jmp	CompileFunction1Arg
FNC_HASINCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			fmul	FltGarb
			fadd	Flt1
			call	TestGtZero
			fsqrt
			fstp	FltGarb2
			fld	FltGarb
			fadd	FltGarb2
			call	DoLog
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HACOS(EXPR) returns the inverted hyperbolic arc cosine of EXPR
			align	8
FNC_HACOS:		mov	eax,WRAP_FNC_HACOS
			jmp	CompileFunction1Arg
FNC_HACOSCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			fmul	FltGarb
			fsub	Flt1
			call	TestGtZero
			fsqrt
			fstp	FltGarb2
			fld	FltGarb
			fadd	FltGarb2
			call	DoLog
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HATN(EXPR) returns the inverted hyperbolic tangent of EXPR
			align	8
FNC_HATN:		mov	eax,WRAP_FNC_HATN
			jmp	CompileFunction1Arg
FNC_HATNCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld1
			fadd	FltGarb
			fstp	FltGarb2
			fld1
			fsub	FltGarb
			call	TestZero
			fstp	FltGarb3
			fld	FltGarb2
			fdiv	FltGarb3
			call	DoLog
			fdiv	Flt2
			jmp	FinalizeFncCOMP
	
;-----------------------------------------------------------------------------------------------
; HASEC(EXPR) returns the inverted hyperbolic secant of EXPR
			align	8
FNC_HASEC:		mov	eax,WRAP_FNC_HASEC
			jmp	CompileFunction1Arg
FNC_HASECCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			call	TestZero
			fstp	FltGarb
			fld	FltGarb
			fchs
			fmul	FltGarb
			fadd	Flt1
			call	TestGtZero
			fsqrt
			fadd	Flt1
			fdiv	FltGarb
			call	DoLog
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HACOSEC(EXPR) returns the inverted hyperbolic cosecant of EXPR
			align	8
FNC_HACOSEC:		mov	eax,WRAP_FNC_HACOSEC
			jmp	CompileFunction1Arg
FNC_HACOSECCOMP:	call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			call	TestZero
			fstp	FltGarb
			fld	FltGarb
			fmul	FltGarb
			fadd	Flt1
			call	TestGtZero
			fsqrt
			fstp	FltGarb2
			fld	FltGarb
			call	SgnFlt
			fmul	FltGarb2
			fadd	Flt1
			fdiv	FltGarb
			call	DoLog
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; HCOATN(EXPR) returns the inverted hyperbolic cotangent of EXPR
			align	8
FNC_HCOATN:		mov	eax,WRAP_FNC_HCOATN
			jmp	CompileFunction1Arg
FNC_HCOATNCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstp	FltGarb
			fld	FltGarb
			fsub	Flt1
			call	TestZero
			fstp	FltGarb2
			fld	FltGarb
			fadd	Flt1
			fdiv	FltGarb2
			call	DoLog
			fdiv	Flt2
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; LOGN(EXPR1,EXPR2) returns the natural logarithm of EXPR1 in EXPR2 base
			align	8
FNC_LOGN:		mov	eax,WRAP_FNC_LOGN
			jmp	CompileFunction2Arg
FNC_LOGNCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			call	DoLog
			fstp	FltGarb
			push	dword ptr [FltGarb]
			push	dword ptr [FltGarb+4]
			call	GetFNCLastArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			call	DoLog
			call	TestZero
			fstp	FltGarb
			pop	dword ptr [FltGarb2+4]
			pop	dword ptr [FltGarb2]
			fld	FltGarb2
			fdiv	FltGarb
			jmp	FinalizeFncCOMP
			align	8
E29:			mov	edi,offset Err29	; Error: <'(' statement expected>
			jmp	DispError
			align	8
E30:			mov	edi,offset Err30	; Error: <')' statement expected>
			jmp	DispError

;-----------------------------------------------------------------------------------------------
; FIX(EXPR) returns the integer part of EXPR
			align	8
FNC_FIX:		invoke	PokeOperStack,VAR_INTEGER
			mov	eax,WRAP_FNC_FIX
			jmp	CompileFunction1Arg
FNC_FIXCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstcw	ExpFPUState1
			push	eax
			mov	ax,ExpFPUState1
			or	ax,110000000000b	; Round toward zero
			mov	ExpFPUState2,ax
			pop	eax
			fldcw	ExpFPUState2
			frndint
			fldcw	ExpFPUState1
			fistp	FltToInt
			mov	ebx,FltToInt
			invoke	PokeOperStack,VAR_INTEGER
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; INT(EXPR) returns the integer part of EXPR
			align	8
FNC_INT:		invoke	PokeOperStack,VAR_INTEGER
			mov	eax,WRAP_FNC_INT
			jmp	CompileFunction1Arg
FNC_INTCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			call	ConvertIntFromExpr
			fstcw	ExpFPUState1
			push	eax
			push	ebx
			mov	ax,ExpFPUState1
			mov	bx,ax
			and	ebx,100010100000000b	; > Zero ?
			cmp	ebx,100000000000000b
			jne	IntAbsolute
			or	eax,0000110000000000b	; Round toward zero
			jmp	IntIt
			align	8
IntAbsolute:		and	eax,1111001111111111b	; Round down negative numbers
			or	eax,0000010000000000b
IntIt:			mov	ExpFPUState2,ax
			pop	ebx
			pop	eax
			fldcw	ExpFPUState2
			frndint
			fldcw	ExpFPUState1
			fistp	FltToInt
			mov	ebx,FltToInt
			invoke	PokeOperStack,VAR_INTEGER
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; IIF(EXPR1,EXPR2,EXPR3) returns EXPR2 if EXPR1 is true or EXPR3 otherwise.
			align	8
FNC_IIF:		mov	eax,WRAP_FNC_IIF
			jmp	CompileFunction3Arg
FNC_IIFCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			cmp	ExprFinalResult,VAR_FLOAT
			jne	IIfFloat
			ftst
			fstsw	ax
			fstp	FltGarb
			and	eax,100010100000000b
			cmp	eax,100000000000000b
			je	IIFFalse
			jmp	IIFTrue
			align	8
IIfFloat:		test	ebx,ebx
			jz	IIFFalse
; Condition was true: returns first part
IIFTrue:		call	EvalExprCOMP
			invoke	PushOperStack,ExprFinalResult
			cmp	ExprFinalResult,VAR_FLOAT
			je	IIFTrueFloat
			; Save it into stack
			; because of next expr checking (just to correct pointer)
			push	ebx
			call	GetFNCLastArgs
			pop	ebx
			jmp	FinalizeFncCOMP
			align	8
IIFTrueFloat:		fstp	FltGarb
			push	dword ptr [FltGarb]
			push	dword ptr [FltGarb+4]
			call	GetFNCLastArgs
			pop	dword ptr [FltGarb+4]
			pop	dword ptr [FltGarb]
			fld	FltGarb
			jmp	FinalizeFncCOMP
; Condition was false: returns second part
			align	8			
IIFFalse:		call	EvalExprCOMP
			call	GetFNCLastArgs
			invoke	PushOperStack,ExprFinalResult
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; TIME returns milliseconds since Windows started in ebx
			align	8
FNC_TICK:		mov	eax,WRAP_FNC_TICK
			jmp	CompileFunction1Arg
FNC_TICKCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			jne	E42
			call	GetTick
			invoke	PushOperStack,VAR_INTEGER
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; SIZE returns amount of free Basic text memory in ebx
			align	8
FNC_SIZE:		mov	eax,WRAP_FNC_SIZE
			jmp	CompileFunction1Arg
FNC_SIZECOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			jne	E42
			mov	ebx,LineNum
			sub	ebx,TxtEnd
			invoke	PushOperStack,VAR_INTEGER
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; LINE returns in ebx the current line number
			align	8
FNC_LINE:		mov	eax,WRAP_FNC_LINE
			jmp	CompileFunction1Arg
FNC_LINECOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			jne	E42
			mov	ebx,SaveLineNumber
			cmp	ebx,-1			; No run yet ?
			jne	@F
			xor	ebx,ebx			; Then report 0
@@:			invoke	PushOperStack,VAR_INTEGER
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; FALSE returns 0
			align	8
FNC_FALSE:		mov	eax,WRAP_FNC_FALSE
			jmp	CompileFunction1Arg
FNC_FALSECOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			jne	E42
			xor	ebx,ebx
			invoke	PushOperStack,VAR_INTEGER
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; TRUE returns 1
			align	8
FNC_TRUE:		mov	eax,WRAP_FNC_TRUE
			jmp	CompileFunction1Arg
FNC_TRUECOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			jne	E42
			mov	ebx,1
			invoke	PushOperStack,VAR_INTEGER
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; {PI} returns PI in st(0)
			align	8
FNC_PI:			mov	eax,WRAP_FNC_PI
			jmp	CompileFunction1Arg
FNC_PICOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			jne	E42
			fldpi
			invoke	PushOperStack,VAR_FLOAT
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; {e} returns e in st(0)
			align	8
FNC_E:			mov	eax,WRAP_FNC_E
			jmp	CompileFunction1Arg
FNC_ECOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			jne	E42
			fld	EConst
			invoke	PushOperStack,VAR_FLOAT
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; INKEY returns a pressed or 0 (non blocking)
			align	8
FNC_INKEY:		mov	eax,WRAP_FNC_INKEY
			jmp	CompileFunction1Arg
FNC_INKEYCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			jne	E42
			push	edx
			mov	dtemp1,0
			invoke	PeekConsoleInput,hStdIn,addr EventBuffer,1,addr dtemp1
			invoke	FlushConsoleInputBuffer,hStdIn
			pop	edx
			xor	ebx,ebx
			cmp	word ptr EventBuffer,KEY_EVENT
			jne	@F
			cmp	byte ptr EventBuffer+4,1
			je	@F
			mov	bl,byte ptr EventBuffer+14
@@:			mov	dtemp1,0
			invoke	PushOperStack,VAR_INTEGER
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; PEEK(EXPR) reads a 8 bits value located in address EXPR located in Basic text area
			align	8
FNC_PEEK:		mov	eax,WRAP_FNC_PEEK
			jmp	CompileFunction1Arg
FNC_PEEKCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			cmp	ebx,MEMDATSIZE		; Inside Basic text memory ?
			jae	E41			; No, error <Memory value overflow>
			add	ebx,DatStart
			movzx	ebx,byte ptr [ebx]	; ebx = 8 bits value
			jmp	FinalizeFncCOMP
			align	8
E27:			mov	edi,offset Err27	; Yes <Integer value expected>
			jmp	DispError

;-----------------------------------------------------------------------------------------------
; PEEKW(EXPR) reads a 16 bits value located in address EXPR located in Basic text area
			align	8
FNC_PEEKW:		mov	eax,WRAP_FNC_PEEKW
			jmp	CompileFunction1Arg
FNC_PEEKWCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			cmp	ebx,MEMDATSIZE-2	; Inside Basic text memory ? (-2 for a word)
			ja	E41			; No, error <Memory value overflow>
			add	ebx,DatStart
			movzx	ebx,word ptr [ebx]	; ebx = 8 bits value
			jmp	FinalizeFncCOMP
			align	8

;-----------------------------------------------------------------------------------------------
; PEEKL(EXPR) reads a 32 bits value located in address EXPR located in Basic text area
			align	8
FNC_PEEKL:		mov	eax,WRAP_FNC_PEEKL
			jmp	CompileFunction1Arg
FNC_PEEKLCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			cmp	ebx,MEMDATSIZE-4	; Inside Basic text memory ? (-4 for a dword)
			ja	E41			; No, error <Memory value overflow>
			add	ebx,DatStart
			mov	ebx,[ebx]		; ebx = 8 bits value
			jmp	FinalizeFncCOMP
			align	8

;-----------------------------------------------------------------------------------------------
; CHR(EXPR) returns ASCII value of an integer.
			align	8
FNC_CHR:		mov	eax,WRAP_FNC_CHR
			jmp	CompileFunction1Arg
FNC_CHRCOMP:		call	GetFNCArgs
			cmp	ExprFinalResult,VAR_NOFNCARG
			je	E37
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			mov	EmptyChr,bl
			push	edx
			mov	ecx,1
			lea	edx,EmptyChr
			call	PutString
			pop	edx
			invoke	PokeOperStack,VAR_STRING
			jmp	FinalizeFncCOMP

;-----------------------------------------------------------------------------------------------
; Support routines
;-----------------------------------------------------------------------------------------------

;-----------------------------------------------------------------------------------------------
; Load an integer into fpu when returned from evalexpr()
			align	8
ConvertIntFromExpr:	cmp	ExprFinalResult,VAR_FLOAT
			je	NoIntConvert
			mov	FltFromInt,ebx
			fild	FltFromInt
			invoke	PokeOperStack,VAR_FLOAT
NoIntConvert:		ret

;-----------------------------------------------------------------------------------------------
; Calculate neperian logarithm of st(0)
			align	8
DoLog:			call	TestGtZero		; Don't calculate
			fldln2				; 0 or negative logarithms
			fxch	st(1)
			fyl2x
			ret

;-----------------------------------------------------------------------------------------------
; Exponentiate e by st(0)
			align	8
DoExp:			fldl2e
			fmulp	st(1),st(0)
			fld	st(0)
			frndint
			fxch	st(1)
			fsub	st(0),st(1)
			f2xm1
			fld1
			faddp	st(1),st(0)
			fscale
			fstp	st(1)
			ret

;-----------------------------------------------------------------------------------------------
; Return the sign of a fp number.
			align	8
SgnFlt:			push	eax
			ftst
			fstsw	ax
			mov	bx,ax
			fstp	FltGarbSgn
			pop	eax
			and	bx,100010100000000b
			jz	SgnFlt_Return1
			cmp	bx,000000100000000b
			je	SgnFlt_ReturnM1
			cmp	bx,100000000000000b
			je	SgnFlt_Return0
			jmp	E24
			align	8
SgnFlt_ReturnM1:	fld1
			fchs
			ret
			align	8
SgnFlt_Return0:		fldz
			ret
			align	8
SgnFlt_Return1:		fld1
			ret

;-----------------------------------------------------------------------------------------------
; Test a fp against 0
			align	8
TestZero:		push	eax
			ftst
			fstsw	ax
			and	eax,100010100000000b
			cmp	eax,100000000000000b
			pop	eax
			je	ExprDivZero		; Must be > zero
			ret

;-----------------------------------------------------------------------------------------------
; Test a fp against >0
			align	8
TestGtZero:		push	eax
			ftst
			fstsw	ax
			and	eax,100010100000000b
			pop	eax
			jnz	E18
			ret

;-----------------------------------------------------------------------------------------------
; Tests event presence in console input buffer.
; In case of event, jumps to TestKeyb, else return to caller
			align	8
TestInpBuf:		push	edx
			mov	dtemp1,0
			invoke	GetNumberOfConsoleInputEvents,hStdIn,addr dtemp1
			call	TestApiError
			mov	ebx,dtemp1
			test	ebx,ebx			; Events in input buffer ? 
			jnz	TK1			; Yes, read it  
			pop	edx			; No, return to caller with ebx = 0
			ret

;-----------------------------------------------------------------------------------------------
; Test keyboard key pressed. If pressed, the scan code is in bx, if not pressed, bx = 0
			align	8
TestKeyb:		; [registers used ecx, eax (API call)]
			push	edx
TK1:			xor	ebx,ebx				; bx = keyscan number or 0
			invoke	ReadConsoleInput,hStdIn,addr EventBuffer,1,addr dtemp1
			call	TestApiError
			cmp	dword ptr dtemp1,0		; Events in input buffer ?
			je	TK2				; No, return to caller
			cmp	word ptr EventBuffer,KEY_EVENT	; Yes, Is a Keyboard Event ?
			jne	TK2				; no : send to valhalla
			cmp	byte ptr EventBuffer+4,1	; Is a keypress ?
			je	@F				; Yes, save it in bl
			mov	al,byte ptr EventBuffer+14	; No, save key release value for Alt+num case
			mov	btemp1,al
			jmp	TK2				; and return with bx=0
			align	8
@@:			mov	bl,byte ptr EventBuffer+14
TK2:			pop	edx
			ret

;-----------------------------------------------------------------------------------------------
; LineInput processes char. arrows and edit keys in the line editor.
; Alt+num entry displays the corresponding ASCII char.
			align	8
LineInput:		; Event loop
			call	TestKeyb			; Keyboard event ?
			cmp	btemp1,0			; Release key = 0 ?
			je	KR1				; Yes, it's a keypress, continue
			cmp	btemp2,1			; No, previous keyboard entry was an Alt+number ?
			je	@F				;    Yes, released code is an ASCII code
			mov	btemp1,0			;    No, reset KeyRelease flag and check next key
			jmp	LineInput
			align	8
@@:			mov	bl,btemp1			;    Yes,
			jmp	DispChar			;    print char
			align	8
KR1:			test	bl,bl
			jnz	KR2				; if bl=0 (no ASCII key pressed)
			mov	al,EventBuffer+12		; Test if a key of Alt+num is valid
			cmp	byte ptr EventBuffer+4,1
			jne	LineInput
			cmp	byte ptr EventBuffer+4,1	; Only available at keydown
			jne	KeyDownKeys
			cmp	al,71				; Home ?
			je	HomeKey
			cmp	al,79				; End ?
			je	EndKey
			cmp	al,75				; Left arrow ?
			je	LeftArrowKey
			cmp	al,77				; Right arrow ?
			je	RightArrowKey
			cmp	al,82				; Insert ?
			je	ToggleInsertMode
			cmp	al,83				; Delete ?
			je	DeleteKey
KeyDownKeys:		cmp	al,71				; Key '0' to '9' From 71 to 82 except 74 & 78
			jb	@F
			cmp	al,82
			ja	@F
			cmp	al,74
			je	@F
			cmp	al,78
			je	@F
			mov	btemp2,1			; OK, it's a valid key, set Alt+num flag
@@:			jmp	LineInput			; and wait for next key
			align	8
KR2:			cmp	bl,13				;     = CR ?
			je	EL0
			cmp	bl,8				;     = BackSpace ?
			je	EL8
			cmp	bl,27				;     = ESC ?
			je	CTC				;       Leave and ignore current edited line
			jmp	DispChar			; If none of above cases, it's a letter key, print it
			; Execute control char.
			align	8
EL0:			mov	eax,LineBuffer			; [CR] Add a CR (Odh) to end of line and return to caller
			add	eax,LineLen
			mov	byte ptr [eax],0dh
CTC:			push	ebx				; save ESC flag, if exists
			or	si,-1				; flag for End Of Line routine
			call	EndKey				; Use END key routine to find end of line cursor position
			xor	si,si
			call	CrLf
			mov	btemp2,0			; Reset Atl+num flag
			pop	ebx
			ret					; End of LineInput. Return to caller
			align	8
LeftArrowKey:		dec	LinePtr				; [LEFT ARROW]
			jnz	@F
			inc	LinePtr				; 1st. char. Impossible to go left
			jmp	LineInput
			align	8
@@:			cmp	Csbi_CursorPositionX,0		; 1st. position in the line ?
			jne	@F				; No
			dec	Csbi_CursorPositionY		; Yes : Go to end of previous line
			mov	cx,Csbi_MaximumWindowSizeX
			mov	Csbi_CursorPositionX,cx
@@:			dec	Csbi_CursorPositionX		; No exceptions : X-1
			jmp	InputCP				; Update cursor position & LineInput
			align	8
RightArrowKey:		mov	eax,LinePtr			; [Right Arrow]
			inc	eax
			cmp	LineLen,eax			; LineLen >= LinePtr ?
			jb	LineInput			; No, forget & LineInput
@@:			mov	LinePtr,eax			; Yes, update LinePtr
			mov	ax,Csbi_WindowRight
			mov	cx,Csbi_CursorPositionY
			cmp	Csbi_CursorPositionX,ax		; Csbi_CursorPositionX inside console X limit ?
			jb	@F				; Yes, go to Right
			mov	Csbi_CursorPositionX,-1		; No, Csbi_CursorPositionX=0 (-1+increment)
			inc	cx
			cmp	cx,Csbi_WindowBottom		; Csbi_CursorPositionY+1 inside console Y limit ?
			jb	@F				; Yes, Csbi_CursorPositionY+1 & update cursor position
			call	ScrollUp			; No, Scroll up
			mov	cx, Csbi_WindowBottom
@@:			inc	Csbi_CursorPositionX		; Update cursor position
			mov	Csbi_CursorPositionY,cx
			jmp	InputCP
			align	8
ToggleInsertMode:	xor	InsertMode,1			; [INSERT] Invert InsertMode bit 1
			mov	CursorShape.bVisible,1
			call	SetConCursor
			jmp	LineInput
			align	8
DeleteKey:		mov	eax,LineLen			; [DELETE]
			cmp	eax,LinePtr			; Line Length = LinePtr ?
			jne	@F				; No, we can cancel 1 char
			jmp	LineInput			; Yes, we do nothing
			align	8
@@:			mov	edi,LineBuffer
			add	edi,LinePtr			; edi=buffer ADDR to write char. read by API
			mov	ebx,LineLen
			sub	ebx,LinePtr			; ebx=Nbr. of char. to move left 1 position
			push	dword ptr Csbi_CursorPositionX
			mov	cx,Csbi_CursorPositionX
			inc	cx
			cmp	cx,Csbi_SizeX
			jb	@F
			xor	cx,cx
			inc	Csbi_CursorPositionY
@@:			mov	Csbi_CursorPositionX,cx		; Csbi_CursorPositionX-Y = 1 position right (or CR-LF)
			invoke	ReadConsoleOutputCharacter,hStdOut,edi,ebx,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError			; Read console char. to move & update LineBuf buffer
			pop	dword ptr Csbi_CursorPositionX
			invoke	WriteConsoleOutputCharacter,hStdOut,edi,ebx,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError			; Write char. previously read 1 position right
			dec	LineLen
			jmp	LineInput
			align	8
EndKey:			mov	ecx,LineLen			; [END] Loop from LinePtr to LineLen
			mov	edx,LinePtr			; and update cursor position
			mov	ax,Csbi_CursorPositionX
			mov	bx,Csbi_CursorPositionY
@@:			cmp	edx,ecx
			je	@F
			inc	edx
			inc	ax
			cmp	ax,Csbi_SizeX
			jb	@B
			xor	ax,ax
			inc	bx
			jmp	@B
			align	8
@@:			cmp	si,-1				; Call from CR key ?
			jne	@F				; No
			mov	Csbi_CursorPositionY,bx		; Update end of line Y cursor position before CrLf
			ret					; Ret to CR key routine
			align	8
@@:			mov	LinePtr,ecx
			jmp	ICP
			align	8
HomeKey:		mov	ax,Csbi_CursorPositionX		; [HOME] Loop from LinePtr to top of line
			mov	bx,Csbi_CursorPositionY		; and update cursor position
			mov	edx,LinePtr
@@:			dec	edx
			jz	@F
			dec	ax
			jns	@B
			mov	ax,Csbi_WindowRight
			dec	bx
			jns	@B
@@:			mov	LinePtr,1
			jmp	ICP
			align	8
EL8:			cmp	LinePtr,1			; [BACKSPACE] LinePtr > 1 ?
			je	LineInput
			mov	edi,LineBuffer			; Yes, we can BS
			dec	edi
			add	edi,LinePtr			; edi=buffer ADDR to write char. read by API
			mov	ebx,LineLen
			sub	ebx,LinePtr
			inc	ebx				; ebx=Nbr. of char. to move left 1 position
			invoke	ReadConsoleOutputCharacter,hStdOut,edi,ebx,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError			; Read console char. to move & update LineBuffer buffer
			mov	cx,Csbi_CursorPositionX
			dec	cx
			jns	@F
			mov	cx,Csbi_WindowRight
			dec	Csbi_CursorPositionY
@@:			mov	Csbi_CursorPositionX,cx		; Csbi_CursorPositionX-Y = 1 position right (or CR-LF)      pop     DWORD PTR Csbi_CursorPositionX
			push	edi
			add	edi,ebx
			mov	byte ptr [edi],"X"
			pop	edi
			invoke	WriteConsoleOutputCharacter,hStdOut,edi,ebx,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError			; Write char. previously read 1 position right
			dec	LineLen
			dec	LinePtr
			jmp	InputCP

;-----------------------------------------------------------------------------------------------
; Display a char in line editor.
; Manage auto-insert, CR-LF and scroll. Update cursor position
			align	8
DispChar:		cmp	ebx,9				; Is it a tabulation ?
			jne	NoTabKey
			mov	ebx," " 			; Replace it with a space
NoTabKey:		mov	dtemp2,ebx			; save char. to print
			mov	eax,LineMaxLen			; Line length = maximum length allowed ?
			cmp	eax,LineLen
			jne	@F
			call	Beeep				; Yes : Beep & return to keyboard loop
			jmp	LineInput
			align	8
@@:			inc	LinePtr
			mov	eax,LineLen
			cmp	eax,LinePtr			; Line Length >= LinePtr ?
			jb	RC1				; No, LineLen + 1
			cmp	InsertMode,1			; Insert mode ?
			je	RC2				; No
			mov	ax,Csbi_CursorPositionY		; Yes, calculate position value of Csbi_CursorPositionX-Y
			mov	bx,Csbi_SizeX
			mul	bx
			add	ax,Csbi_CursorPositionX
			inc	ax				; ax=cursor position value
			inc	LineLen
			mov	ebx,LineLen
			sub	ebx,LinePtr			; bx=Nbr. of char. to move 1 position right
			push	eax
			add	ax,bx				; ax= End of line position value
			dec	ax
			cmp	ax,word ptr ConsoleCharNbr	; End of line at last console position ?
			pop	eax
			jb	@F				; No, we can insert 1 char.
			call	ScrollUp			; Yes, Scroll Up,
			sub	ax,Csbi_SizeX			; update ax position value
			dec	Csbi_CursorPositionY
@@:			mov	edi,LineBuffer
			add	edi,LinePtr			; edi=buffer ADDR to store char. read by API
			invoke	ReadConsoleOutputCharacter,hStdOut,edi,ebx,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError			; Read console char. to move & update LineBuffer buffer
			push	dword ptr Csbi_CursorPositionX
			mov	cx,Csbi_CursorPositionX
			inc	cx
			cmp	cx,Csbi_SizeX
			jb	@F
			xor	cx,cx
			inc	Csbi_CursorPositionY
@@:			mov	Csbi_CursorPositionX,cx		; Csbi_CursorPositionX-Y = 1 position right (or CR-LF)
			invoke	WriteConsoleOutputCharacter,hStdOut,edi,ebx,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError			; Write char. previously read 1 position right
			pop	dword ptr Csbi_CursorPositionX
			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			call	TestApiError
			jmp	RC2
			align	8
RC1:			inc	LineLen
RC2:			invoke	WriteConsole,hStdOut,addr dtemp2,1,addr dtemp1,0
			call	TestApiError
			mov	eax,LineBuffer			; Put chr at Ptr position
			dec	eax
			add	eax,LinePtr
			mov	ecx,dtemp2			; Chr. to put
			mov	[eax],cl			; set chr. in LineBuffer at LinePtr position
			mov	ax,Csbi_CursorPositionX
			mov	bx,Csbi_CursorPositionY
			inc	ax
			cmp	ax,Csbi_SizeX			; X cursor position in console limit ?
			jb	ICP				; Yes
			xor	ax,ax
			mov	Csbi_CursorPositionX,ax		; No  CR+LF  Y=Y+1, X=0
			inc	bx
			cmp	bx,Csbi_SizeY			; Y cursor position in console limit ?
			jb	ICP				; Yes
			call	ScrollUp			; No, Scroll Up & update cursor position
			jmp	InputCP
			align	8
ICP:			; X & Y cursor position in console limit
			mov	Csbi_CursorPositionX,ax
			mov	Csbi_CursorPositionY,bx
InputCP:		; Line input Cursor Position (!called by most of editor keys!)
			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			call	TestApiError
			mov	btemp2,0			; Reset Atl+num flag
			jmp	LineInput

;----------------------------------------------------------------------------------------------
; Divides ebx by edx. Result in ecx, Remainder in ebx
			align	8
Divide:			; [register used ecx, eax]
			push	edx				; Preserve edx
			mov	ecx,edx
			xor	edx,edx
			mov	eax,ebx
			test	eax,eax				; Change sign extension
			jns	@F				; if needed
			dec	edx
@@:			test	ecx,ecx				; Can't divide by zero
			jz	@F
			idiv	ecx
@@:			mov	ecx,eax				; Quotient
			mov	ebx,edx				; Remainder
			pop	edx
			ret

;-----------------------------------------------------------------------------------------------
; CheckSign checks the sign of ebx. If +, no change. If -, change sign and invert sign of ch
			align	8
CheckSign:		test	ebx,ebx				; Test s flag
			jns	@F				; If -, change sign, else ret
;-------------------------------------------
; Change sign of ebx & ch inconditionally
ChangeSign:		not	ebx				; Change ebx sign (2 complement)
			inc	ebx
			xor	ch,128				; Change ch sign
@@:			ret

;-----------------------------------------------------------------------------------------------
; CheckSign checks the sign of st(0). If +, no change. If -, change sign and invert sign of ch
			align	8
CheckSignFlt:		push	eax
			ftst
			fstsw	ax
			and	ax,100010100000000b	; mask condition codes
			cmp	ax,000000100000000b	; should be 001 if <0.0
			pop	eax
			jne	@F			; If -, change sign, else ret
;-------------------------------------------
; Change sign of st(0) 
ChangeSignFlt:		fchs
@@:			ret

;-----------------------------------------------------------------------------------------------
; CompareSign checks the sign of ebx and edx.
; If different, ebx and edx are interchanged, if same sign, they are not.
; Either case, ebx and edx are compared to set flags
			align	8
CompareSign:		bswap	ebx
			bswap	edx				; signs are now in bl and dl
			mov	al,bl
			xor	al,dl				; Same sign ?
			bswap	ebx				; back to original values
			bswap	edx				; (bswap doesn't affect flags)
			jns	@F				; Yes, only compare
			xchg	edx,ebx				; No, exchange and compare
@@:			cmp	ebx,edx
			ret

;-----------------------------------------------------------------------------------------------
; CompareSignFlt checks the sign of FltTerm1 and FltTerm2.
; If different, ebx and edx are interchanged, if same sign, they are not.
; Either case, ebx and edx are compared to set flags
			align	8
CompareSignFlt:		push	eax
			fld	FltTerm1
			fcom	FltTerm2
			fstsw	ax
			and	eax,100010100000000b
			cmp	eax,000000100000000b		; lower
			jne	FltNotLower
			or	eax,-1
			test	eax,eax
			pop	eax
			ret
			align	8
FltNotLower:		cmp	eax,100000000000000b		; equal
			jne	FltNotEqual
			xor	eax,eax
			test	eax,eax
			pop	eax
			ret
			align	8
FltNotEqual:		mov	eax,1
			test	eax,eax
			pop	eax
			ret

;-----------------------------------------------------------------------------------------------
; VarEquExpr expects a variable, followed by an equal sign and then an expression
; Evaluates the expression and sets the variable to the value
			align	8
VarEquExpr:		call	TestVar				; A variable is found ?
			jnc	@F
			mov	edi,offset Err8			; No, error <Keyword or variable expected>
			jmp	DispError
			align	8
@@:			push	ebx				; Save address of variable
			mov	ah,'='
			call	SkipSpaces			; Sign '=' found ?
			jz	@F				; Yes, evaluate expression
			mov	edi,offset Err9			; No, error <Sign '=' expected>
			jmp	DispError
			align	8
@@:			call	EvalExpr
			pop	ebx
			ret

;-----------------------------------------------------------------------------------------------
; CmdEnd checks the end of a command. If command ends with ':', execution continues,
; if command ends with a CR, finds the next line and continues from there
			align	8
CmdEnd:			;mov	ah,0dh				; Found a CR ?
			;call	SkipSpaces
			;jnz	@F				; No, test REM
			pop	eax				; Yes, remove ret address from stack
			inc	edx
			jmp	NextLine			; and run next line
			align	8
@@:			;mov	ah,"'"				; Found a REM ?
			;call	SkipSpaces
			;jnz	@F				; No, return to the caller
			;pop	eax				; Yes, skip end of line
			;jmp	NoThenUnVerified
			;align	8
@@:			;ret

;----------------------------------------------------------------------------------------------
; Report an error if an argument is found
			align	8
TestNoArguments:	call	TrimSpaces
			cmp	byte ptr [edx],0dh
			je	@F
			cmp	byte ptr [edx],"'"
			je	@F
			cmp	byte ptr [edx],":"
			jne	E42
@@:			ret

;----------------------------------------------------------------------------------------------
; Report an error if no arguments are found
			align	8
TestArgumentsExpected:	call	TrimSpaces
			cmp	byte ptr [edx],0dh
			je	E37
			cmp	byte ptr [edx],":"
			je	E37
			cmp	byte ptr [edx],"'"
			je	E37
			ret

;----------------------------------------------------------------------------------------------
			align	8
DispError:		mov	ExecMode,EXECMODE_INTERPRET
			push	edi				; save offset of error message
			cmp	Csbi_CursorPositionX,0		; Message must be printed at top of line
			je	@F
			call	CrLf
@@:			mov	edx,offset ErrorTxt
			xor	al,al				; Message end delimiter
			call	DispStr				; Display 'error'
			cmp	LineNumber,0			; Line number to print ?
			je	@F				; No, CrLf & return to 'Ready'
			mov	edx,offset ErrorIn		; Yes, print ' in '
			xor	al,al
			call	DispStr
			xor	cl,cl				; No spaces before line number
			mov	ebx,LineNumber
			call	DispNumber			; Print Line number
@@:			xor	al,al
			mov	edx,offset Colon		; print " : "
			call	DispStr
			xor	al,al
			pop	edx				; edx = Offset error message
			call	DispStr
			call	CrLf
			jmp	Ready				; Reset pointers & LineInput

;----------------------------------------------------------------------------------------------
; FindLine looks for a same line number as ebx in Basic text
; edx is used as text pointer.
; If line number is found :
;   edx points on the line first byte. Flags are NC & Z
; if an higher line number is found :
;   edx points on the line first byte. Flags are NC & NZ
; if End of Basic Text reached:
;   edx points on Basic text start. Flags are C et NZ
;
; 'FindNextLine' looks for End of line (0dh) from the line beginning and jump to LineNumCompare.
; 'FindEndOfLine' does the same but start searching after line number.
			align	8
FindLine:		test	ebx,ebx				; Line number Sign bit must be = 0
			jns	@F				; Yes, OK
			mov	edi,offset Err1			; No, error : Invalid line number
			jmp	DispError
			align	8
@@:			mov	edx,TxtStart			; edx = Basic text start
LineNumCompare:		push	ebx				; Save line number to find
			mov	ebx,TxtEnd			; Check if Basic text end passed
			dec	ebx
			cmp	ebx,edx
			pop	ebx				; Recover line number
			jb	@F				; Yes, passed end : C & NZ
			mov	eax,[edx]			; No, eax = line number found
			cmp	eax,ebx				; eax > line number to find ?
			jc	FindNextLine			; no, loop to following line number in Basic text
@@:			ret					; Yes, line number found : NC & Z; not found : NC &,NZ
			align	8
FindNextLine:		add	edx,4
@@:			inc	edx
FindEndOfLine:		mov	al,[edx]
			cmp	al,0dh
			jnz	@B				; Loop while not find a CR
			inc	edx
			jmp	LineNumCompare			; CHECK IF END OF TEXT

; Pre-compiled version
			align	8
FindLineComp:		test	ebx,ebx
			jns	@F
			mov	edi,offset Err1	
			jmp	DispError
			align	8
@@:			mov	edx,CompStart
LineNumCompareComp:	push	ebx
			mov	ebx,CompEnd
			dec	ebx
			cmp	ebx,edx
			pop	ebx
			jb	@F
			mov	eax,[edx]
			cmp	eax,ebx
			jc	FindNextLineComp
@@:			ret
			align	8
FindNextLineComp:	add	edx,11
			jmp	LineNumCompareComp
			align	8
FindEndOfLineComp:	add	edx,11-6
			jmp	LineNumCompareComp
			align	8
FindNextLineCompEnd:	inc	edx
			jmp	LineNumCompareComp

;-----------------------------------------------------------------------------------------------
; DispStr prints a string pointed to by edx. Returns to caller when either a Odh is found &
; printed or when the next byte is the same as what was in al (given by the caller)
; [Register used ax, ebx, ecx, edx, esi, edi]
; DispNumStr displays a BCPL string (Number as DWORD,String itself)
			align	8
DispNumStr:		mov	ecx,[edx]			; Get number of chars
			add	edx,4
			mov	ebx,PrintBuffer
			mov	edi,ebx
NumPS1:			mov	al,[edx]
			mov	[ebx],al
			inc	ebx
			inc	edx
			dec	ah				; Number of char to pad
			dec	ecx
			jnz	NumPS1
			test	ah,ah
			jle	@F
PadStr:			mov	byte ptr [ebx]," "
			inc	ebx
			dec	ah
			jnz	PadStr
			push	edx
			sub	ebx,edi
			jz	PS3
			mov	edi,edx
			mov	edx,ebx
			jmp	PrintStr
			align	8
DispStr:		mov	edi,edx				; edi will be used to count char. to print
			mov	ebx,PrintBuffer			; Chars. to print are transfered in PrintBuffer
PS1:			mov	ah,[edx]			; Get a char.
			cmp	ah,al				; same as al flag ?
			je	@F				; Yes, end of tranfer
			cmp	ah,0ah
			jne	PassCRLF
			push	eax
			push	ebx
			push	ecx
			push	edi
			push	edx
			call	@F				; Print it
			call	CrLf				; Add crlf
			pop	edx
			pop	edi
			pop	ecx
			pop	ebx
			pop	eax
			inc	edx				; Pass over
			mov	edi,edx				; reset chars counter
			mov	ebx,PrintBuffer			; Chars. to print are transfered in PrintBuffer
			jmp	PS1
			align	8
PassCRLF:		mov	[ebx],ah			; None of above cases, add char. in print buffer
			inc	ebx
			inc	edx
			jmp	PS1				; transfer loop back
			align	8
@@:			push	edx
			sub	edx,edi				; edx = Number of char. to print
			jz	PS3				; No char. to print
PrintStr:		push	eax				; Does the line to print need a scroll ?
			xchg	edx,edi				; edi = Number of char. to print
			mov	cx,Csbi_WindowRight
			mov	ebx,ecx
			inc	ebx				; ebx becomes Csbi-SizeX
			sub	cx,Csbi_CursorPositionX		; cx = available space on the current line
			cmp	cx,di				; Length of line to print > available space on the current line ?
			jnc	PS2				; No, OK to print
			mov	ax,Csbi_WindowBottom		; Yes...
			sub	ax,Csbi_CursorPositionY
			mul	bx
			add	ax,cx				; ... ax = char. availables from cursor to end of screen
			sub	ax,di				; Line length to print > ax ?
			jnc	PS2				; No, OK to print
@@:			call	ScrollUp			; Yes, Scroll up necessary lines and update cursor position
			dec	Csbi_CursorPositionY		; to get room enough to print line
			add	ax,bx
			jnc	@B
PS2:			pop	eax
			mov	bh,ah				; ah will be modified by API calls
			; I used these 2 API to print text because 'SetConsoleTextAttribute' and 'WriteConsoleA' didn't
			; work correctly with automatic CR and scroll up
			invoke	FillConsoleOutputAttribute,hStdOut,dword ptr ColorsCurrent,edi,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError			; set BackGround & Foreground colors
			invoke	WriteConsoleOutputCharacter,hStdOut,dword ptr PrintBuffer,edi,dword ptr Csbi_CursorPositionX,addr dtemp1
			call	TestApiError			; Print line of chars.
			mov	si,Csbi_CursorPositionX		; Calculate new physical cursor position in buffer
			mov	ax,Csbi_CursorPositionY
			mov	cx,Csbi_SizeX
			mul	cx
			add	ax,si				; ax = old position
			add	ax,di				; now, ax=new physical position
			div	cl				; Result of ax/cl in al, remainder in ah
			mov	byte ptr Csbi_CursorPositionY,al
			mov	byte ptr Csbi_CursorPositionX,ah
			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			call	TestApiError			; Update cursor position
			cmp	bh,0dh
			jnz	PS3
			call	CrLf
PS3:			pop	edx
			inc	edx
			ret

;-----------------------------------------------------------------------------------------------
; Print a string between quotes.
; Perform a CR without LF if '\' is found
			align	8
QuotedString:		xor	al,al
			jmp	DispStr

;-----------------------------------------------------------------------------------------------
; DispNumber prints the number in edx. Leading blanks are added if needed to pad the number
; of spaces to the value in cl. If the number of digits is larger than the cl value, all digits
; are printed anyway. Negative sign is also printed and counted. Positive sign is not
; On entry, ebx = binary number, cl = spaces
			align	8
DispNumber:		push	edx
			mov	edx,10				; For divide by 10
			push	edx				; SAVE AS A FLAG
			mov	ch,dh				; ch = sign (0)
			dec	cl				; cl=SPACES
			test	ebx,ebx				; Do not check sign if number is 0
			jz	@F				; (this won't print -0)
			call	CheckSign			; Check sign, change it if negative
			jns	@F				; No sign
			mov	ch,"-"				;
			dec	cl				; '-' takes 1 space
@@:			push	ecx				; Save sign & space
@@:			call	Divide				; Divides ebx by edx (10). Result in ecx, Remainder in ebx
			test	ecx,ecx				; Result > 0 ?
			jz	@F				; No,
			pop	eax				; Yes, GET SIGN AND SPACE COUNT
			push	ebx				; Save remainder
			dec	al				; dec space counter
			push	eax				; Save new sign & space counter
			mov	ebx,ecx				; Move result to ebx
			jmp	@B				; and Divide by 10 again
			align	8
@@:			mov	edi,offset dtemp1		; Buffer to print the number
			pop	ecx				; Now, all digits are in the stack
@@:			dec	cl				; Dec space counter
			test	cl,cl				; Leading blanks ?
			js	@F				; No, We can print number
			mov	al,32				; Yes, we print a space
			stosb					; in [edi]
			jmp	@B
			align	8
@@:			test	ch,ch
			jz	@F
			mov	al,ch				; PRINT SIGN '-'
			stosb
@@:			mov	dl,bl				; Take off each digits from stack and print it
@@:			mov	al,dl
			cmp	al,10				; if stack value = 10 then display number & return
			pop	edx
			jz	@F
			add	al,48				; convert digit to ASCII
			stosb					; & store it in display buffer
			jmp	@B				; Loop to next digit
			align	8
@@:			xor	al,al
			mov	[edi],al			; Display number
			push	edx
			mov	edx,offset dtemp1
			call	DispStr
			pop	edx
			ret

;-----------------------------------------------------------------------------------------------
; DispFloat prints the floating point number in st(0).
; On entry, st(0) = fp number
			align	8
DispFloat:		push	edx
			fstp	FltVal				; Store fp value
			; Convert it
			invoke	FloatToString,FltVal,addr DblTemp
			mov	edx,offset DblTemp
			xor	al,al
			; Print it
			call	DispStr
			pop	edx
			ret

;-----------------------------------------------------------------------------------------------
; Display line number & Basic text line
			align	8
DispBasicLine:		mov	ebx,[edx]
			add	edx,4				; Skip line pointer
			mov	cl,ch				; Display ch digits Line number (5 for LIST, 0 for EDIT) 
			push	ecx				; Save ch
			call	DispNumber
			mov	ebp,edi				; ebp = line char. counter for EDIT    
			pop	ecx
			mov	al,[edx]			; If line text starts with a space (indent)
			cmp	al," "				; don't print additional space
			jz	@F
			mov	Disp1Char," "			; Followed by a space
			push	edx
			mov	edx,offset Disp1Char
			xor	al,al
			call	DispStr
			inc	ebp
			pop	edx
@@:			mov	al,0dh				; And then the text line
			call	DispStr
			add	ebp,edi
R7:			ret

;-----------------------------------------------------------------------------------------------
; MoveUp moves a block up from address edx to address ecx until edx = ebx
			align	8
MoveUp:			cmp	edx,ebx				; if edx = ebx : Return to caller
			jz	@F
			mov	al,[edx]
			inc	edx
			mov	[ecx],al
			inc	ecx
			jmp	MoveUp				; Loop back until done

;-----------------------------------------------------------------------------------------------
; MoveDown moves a block down from address edx to address ebx until edx = ecx
			align	8
MoveDown:		cmp	edx,ecx				; Idem MoveUp
			jz	@F
			dec	edx
			dec	ebx
			mov	al,[edx]
			mov	[ebx],al
			jmp	MoveDown
			align	8
@@:			mov	SaveLineNumber,-1		; Invalid CONT command
			ret

;-----------------------------------------------------------------------------------------------
; Check a space char in lastest position
			align	8
CheckLastSpace:		mov	al,[edx-1]
			cmp	al,32
			ret

;-----------------------------------------------------------------------------------------------
			align	8
FindCloseBracket:	mov	ah,")"
			jmp	SkipSpaces
			align	8
FindComma:		mov	ah,","				; Next char. is a comma ?
;-------------------------------------------
; Skip spaces and test if 1st. [edx]char. = ah. If yes, z = 1
;           [register used : ax]
SkipSpaces:		mov	al,[edx]
			cmp	al,32				; [edx]=" "?
			jne	@F				; No, Test ah before end of loop
			inc	edx				; Yes, loop to next char.
			jmp	SkipSpaces
			align	8
@@:			inc	edx				; edx+1 if ah test OK
			cmp	al,ah				; [edx]=ah ?
			je	@F				; Yes, Return with edx+1, pointing on next char.
			dec	edx				; No , edx points on ah char.
@@:			ret

;-------------------------------------------
; Trim spaces 
TrimSpaces:		mov	al,[edx]
			cmp	al,32				; [edx]=" "?
			jne	@F				; No, Test ah before end of loop
			inc	edx				; Yes, loop to next char.
			jmp	SkipSpaces
			align	8
@@:			ret

;-----------------------------------------------------------------------------------------------
; NextCmd test end of current command. if next char. is ':' or CR, execution continues,
; else error : <':' or end of line expected>
			align	8
NextCmd:		call	CmdEnd				; If ':' or CR found, execution continues without return
			mov	edi,offset Err7			; If a ret occur from CmdEnd, Error.
			jmp	DispError

;-----------------------------------------------------------------------------------------------
; Prepares a title string with '.MBI' extension and '0' delimiter for LOAD & SAVE commands
			align	8
SaveFileTitle:		mov	ebx,FileName
@@:			mov	al,[edx]
			cmp	al,"*"			; Break in illegal char
			je	E28
			cmp	al,"?"
			je	E28
			cmp	al,34
			je	E28
			cmp	al,"<"
			je	E28
			cmp	al,">"
			je	E28
			cmp	al,"|"
			je	E28
			cmp	al,0dh
			jz	@F
			mov	[ebx],al
			inc	ebx
			inc	edx
			jmp	@B
			align	8
@@:			mov	byte ptr [ebx],0	; End of string delimiter
			jmp	AppendMBasicExtension
			align	8
E28:			mov	edi,offset Err28	; Error: <Illegal char found>
			jmp	DispError

;-----------------------------------------------------------------------------------------------
; This subroutine is called after each API call and display a simple MessageBox with
; 'API Error' if an error occurs.
; Mainly used for debugging stage.
; All 'call TestApiError' can be removed to save execution time
			align	8
TestApiError:		test	eax,eax
			jz	DisplayError
			ret

;------------------------------------------------------------------------------------------------    
; Display the API error number and exit.
			align	8
DisplayError:		invoke	StdOut,addr APIText
			invoke	GetLastError
			invoke	dwtoa,eax,addr NbrBlock
			invoke	StdOut,addr NbrBlock
			invoke	ExitProcess,0

;----------------------------------------------------------------------------------------------
; See CMD_BEEP routine
			align	8
Beeep:			invoke	Beep,440,250		; 440 Hz. 250 Msec. (or default Windows alert sound)
			ret

;----------------------------------------------------------------------------------------------- 
; Performs a CR+LF vith eventual scroll up and update cursor position
			align	8
CrLf:			push	edx
			mov	Csbi_CursorPositionX,0		; Csbi_CursorPositionX = 0
			mov	ax,Csbi_CursorPositionY
			cmp	ax,Csbi_WindowBottom		; Csbi_CursorPositionY wil be over Y screen limit ?
			jb	@F				; No, Csbi_CursorPositionY + 1                         
			call	ScrollUp			; Yes, Scroll Up
			jmp	CR1
			align	8
@@:			inc	Csbi_CursorPositionY
			; Update cursor position
CR1:			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			pop	edx
			ret

;-----------------------------------------------------------------------------------------------
; Redim a new console window with sizes Y (si) and X (di) and update screen colors attributes
; Tests if new size fits in Window's screen sizes
			align	8
RedimWindow:		push	esi
			invoke	GetConsoleScreenBufferInfo,hStdOut,addr Csbi_SizeX
			invoke	GetLargestConsoleWindowSize,hStdOut
			pop	esi
			; X in wtemp1 Y in wtemp2
			mov	dword ptr wtemp1,eax
			cmp	si,wtemp1			; requested width > maximum allowed ?
			ja	E18				; Yes, error <Invalid argument>
			cmp	di,wtemp2			; Same for height
			ja	E18
			mov	ax,si
			mul	di				; ax = Requested Buffer size
			push	eax				; save requested buffer size
			mov	ax,word ptr Csbi_SizeX
			mul	word ptr Csbi_SizeY		; ax = current buffer size
			mov	Csbi_SizeX,si
			mov	Csbi_SizeY,di
			mov	word ptr Csbi_WindowLeft,0	; Update parameters for new window size : X position
			mov	word ptr Csbi_WindowTop,0	; Y position
			dec	si
			dec	di
			mov	Csbi_WindowRight,si		; Bottom-right position X
			mov	Csbi_WindowBottom,di		; Bottom-right position Y
			pop	ebx				; bx = Requested buffer size
			cmp	ax,bx				; Compare it with current buffer
			je	RD1				; If equal sizes, nothing to do, return to caller
			jb	@F				; Current buffer is smaller
			; Current buffer is higher, redim window first, then buffer
			invoke	SetConsoleWindowInfo,hStdOut,1,addr Csbi_WindowLeft
			invoke	SetConsoleScreenBufferSize,hStdOut,dword ptr Csbi_SizeX
			jmp	RD1
			align	8
			; Current buffer is smaller, redim buffer first, then window 
@@:			invoke	SetConsoleScreenBufferSize,hStdOut,dword ptr Csbi_SizeX
			call	TestApiError
			; Invert it
			invoke	SetConsoleWindowInfo,hStdOut,1,addr Csbi_WindowLeft
RD1:			mov	ConsoleCharNbr,ebx		; Save new console screen char. numbers
			; update Csbi buffer
RD3:			invoke	GetConsoleScreenBufferInfo,hStdOut,addr Csbi_SizeX
			xor	eax,eax				; Nothing allowed in input
			invoke	SetConsoleMode,hStdOut,eax
			call	TestApiError
			mov	eax,ENABLE_PROCESSED_INPUT	; Ctrl-C is used to leave MBasic, mouse ignored   
			invoke	SetConsoleMode,hStdIn,eax
			call	TestApiError
			jmp	WE1				; Set screen attributes

;------------------------------------------------------------------------------------------------
			align	8
ScrollUp:		; Scroll up 1 line  [registers used : edx, ecx]
			push	eax
			mov	SMALL_RECT_Left,0	; Init of rectangle borders
			mov	SMALL_RECT_Top,1
			mov	ax,Csbi_SizeX
			mov	SMALL_RECT_Right,ax
			mov	ax,Csbi_SizeY
			mov	SMALL_RECT_Bottom,ax
			mov	eax,ColorsCurrent	; Foreground = 7, Background = 0
 			mov	wtemp1," "		; Char. to fill empty line
			mov	wtemp2,ax		; Colors attributes
			invoke	ScrollConsoleScreenBuffer,hStdOut,addr SMALL_RECT_Left,0,0,addr wtemp1
			call	TestApiError
			pop	eax
			ret

;-----------------------------------------------------------------------------------------------
; WaitLoop waits for ebx milliseconds and returns to caller
			align	8
WaitLoop:		pushad
			invoke	Sleep,ebx
			popad
			ret

;-----------------------------------------------------------------------------------------------
; Return number of milliseconds since Windwos started
			align	8
GetTick:		push	eax
			push	ecx
			push	edx
			push	esi
			push	edi
			invoke	GetTickCount
			mov	ebx,eax
			pop	edi
			pop	esi
			pop	edx
			pop	ecx
			pop	eax
			ret

;-----------------------------------------------------------------------------------------------
; Allocates standard Input and Output handles for a new console screen 
			align	8
AllocCons:		invoke	GetStdHandle,STD_INPUT_HANDLE
			call	TestApiError
			mov	hStdIn,eax
			invoke	GetStdHandle,STD_OUTPUT_HANDLE
			call	TestApiError
			mov	hStdOut,eax
			ret

;-----------------------------------------------------------------------------------------------
; Redim window screen with original editor sizes and colors
			align	8
WinEdit:		mov	eax,EditColors			; Set editor colors
			mov	ColorsCurrent,eax
			invoke	GetLargestConsoleWindowSize,hStdOut
			mov	dword ptr wtemp1,eax
			mov	si,EditSizeX			; and sizes
			mov	di,EditSizeY
			; Correct the values if necessary
			cmp	si,wtemp1
			jna	CorrectConX
			mov	si,wtemp1
CorrectConX:		cmp	di,wtemp2
			jna	CorrectConY
			mov	di,wtemp2
CorrectConY:		mov	EditSizeX,si
			mov	EditSizeY,di
			call	RedimWindow			; Redim console window & buffer
WE1:			mov	eax,ColorsCurrent		; fill console screen with current colors attributes
			xor	ecx,ecx				; Starting at cursor position 0,0
			invoke	FillConsoleOutputAttribute,hStdOut,eax,ConsoleCharNbr,ecx,addr dtemp1
			call	TestApiError
			mov	eax,32				; Char. to fill screen with (space)
			invoke	FillConsoleOutputCharacter,hStdOut,eax,ConsoleCharNbr,0,addr dtemp1
			call	TestApiError			; Fill screen xith spaces
			mov	dword ptr Csbi_CursorPositionX,0
			invoke	SetConsoleCursorPosition,hStdOut,dword ptr Csbi_CursorPositionX
			call	TestApiError			; Set cursor position at 0,0
			invoke	SetConsoleTitle,addr WinTitle
			jmp	TestApiError

;-----------------------------------------------------------------------------------------------
; InitP initialize FOR..NEXT and GOSUB stacks pointers
			align	8
InitP:			xor	eax,eax
			mov	LineNumber,eax		; Init some pointers
			mov	GosLineNum,eax		; To check RETURN without GOSUB
			mov	TrState,al		; Trxx state
			mov	eax,GosubStack		; GosStackPtr on top of GOSUB stack
			mov	GosStackPtr,eax		; GosStackPtr goes up
			mov	ForStackPtr,eax		; ForStackPtr goes down
			mov	eax,IfStack
			mov	IfStackPtr,eax
			mov	dword ptr [eax-8],0
			mov	dword ptr [eax-4],-1
			invoke	FillMem,IfStack,256*8,-1
			ret

;-----------------------------------------------------------------------------------------------
; Test if a variable A to Z or array '@(EXPR)' can be found.
; If found, c=0, ebx=variable address, else c=1 [registers used : ax, ebx]
			align	8
TestVar:		xor	cl,cl
TestVarAddress:		xor	ebx,ebx
			mov	ah,'@'			; Is it a variable ?
			call	SkipSpaces
			jc	VarAddr			; No, return to caller
			jnz	TV1			; Yes, it's a letter variable, process...
			test	cl,cl
			je	WasArrayAddress
			invoke	AddWrapArrayAddress
			call	StoreArrayAddress
			add	ebx,ArrayVar
			mov	cl,WRAP_ARRAYADDRESS
			clc
			ret
WasArrayAddress:	invoke	AddWrapArray
			call	StoreArrayAddress
			add	ebx,ArrayVar
			mov	cl,WRAP_ARRAY
			clc
			ret
StoreArrayAddress:	call	TestParenth		; it's an array @ followed by '(EXP)'
			; Array index must be integer
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
TV2:			clc
			ret
			align	8
VarAddr:		cmp	al,"&"			; Variables address marker
			jne	NotVarAddress
			inc	edx
			or	cl,-1			; Mark it as referenced
			jmp	TestVarAddress		; Check back
NotVarAddress:		stc				; set carry flag
			ret
			align	8
TV1:			sub	al,64			; Variable A-Z = code 1 to 26 (ASCII - 64) ?
			cmp	al,27
			jnc	TV2			; No, Return to caller with c=1
			inc	edx			; Yes, test pointer + 1
			mov	bl,al			;     ebx = variable range (1 to 26)
			dec	bl
			sal	ebx,4			;     ebx = ebx*16 (16 bytes per variable value)
StoreVar:		test	cl,cl
			je	WasVarAddress
			invoke	AddWrapVarAddress,ebx
			add	ebx,AZVar
			mov	cl,WRAP_VARADDRESS
			clc
			ret
			align	8
WasVarAddress:		invoke	AddWrapVar,ebx
			add	ebx,AZVar		;     ebx = address (AZVar-16)+(ebx*16)
			mov	cl,WRAP_VAR
			clc
			ret

;-----------------------------------------------------------------------------------------------
; Test an ASCII integer 32 bits number from [edx]. ebx = binary result or 0 if not.
; [registers used : eax, ecx]
			align	8
TstNum:			push	edi
			xor	ebx,ebx			; bx = binary result
			mov	ch,bh			; ch=0. Char. counter
			xor	eax,eax			; cancel SkipSpaces comparison
			call	SkipSpaces		; al = first char. found
			cmp	al,"0"			; Hex prefix ?
			jne	@F
			cmp	byte ptr [edx+1],"X"
			je	GetHex
			cmp	byte ptr [edx+1],"x"
			jne	@F
GetHex:			; Hex numbers conversion
			inc	edx
			lea	edi,HexTab
ConvertHex:		inc	edx
			mov	al,[edx]
			mov	al,[edi+eax]
			test	al,al			; Invalid char ?
			js	R1
			inc	ch			; Char. counter
			cmp	ch,9			; Overflow
			je	E2
			shl	ebx,4			; XXXXXXXY-> XXXXXXYX
			or	ebx,eax
			jmp	ConvertHex	
			; Conversion loop
@@:			cmp	al,'0'			; char al must be between '0' & '9'.
			jc	R1			; If not, return
			cmp	al,":"
			jnc	R1
			inc	ch			; Char. counter
			push	ecx
			mov	eax,ebx			; bx=(bx*10) + new al char. value
			mov	ecx,10
			push	edx			; edx will be modified by MUL
			mul	ecx
			mov	ebx,eax			; Partial result (bx=bx*10)
			pop	edx
			xor	eax,eax
			mov	al,[edx]		; al = ASCII value '0' to '9'
			sub	al,48			; now in binary value
			inc	edx
			add	ebx,eax			; added do ebx
			mov	al,[edx]
			pop	ecx			; Char. counter
			cmp	ch,11
			jne	@B			; Next char. or error if sign Flag = 1
E2:			mov	edi,offset Err2		; Error : <Numeric value overflow>
			jmp	DispError
			align	8
R1:			pop	edi
			ret

;-----------------------------------------------------------------------------------------------
; PutString directly pushes a string into string memory.
; in: edx=address of string
;     ecx=number of chars to put
; out: ebx=address of string in the strmem
			align	8
PutString:		mov	ebx,LitStringsPtr
			push	ebx
			mov	eax,ebx
			add	eax,MEMSTRINGSIZE
PutStringChar:		mov	al,[edx]
			inc	edx
			cmp	ebx,eax
			jae	E43
			mov	[ebx],al
			inc	ebx
			inc	LitStringsPtr
			dec	ecx
			jnz	PutStringChar
			mov	byte ptr [ebx],0		; Null
			inc	LitStringsPtr
			pop	ebx
			ret

;-----------------------------------------------------------------------------------------------
; Test an ASCII string between quotes
; Return the address of the string in the table or 0
; (Called from TstNumFloat)
			align	8
TstString:		push	ecx
			xor	ecx,ecx
			cmp	al,34
			jne	NotAString
			inc	edx
WriteStringInMem:	mov	ebx,LitStringsPtr
			push	ebx
			mov	ecx,ebx
			add	ecx,MEMSTRINGSIZE
GetString:		mov	al,[edx]
			inc	edx
			cmp	al,34
			je	@F
			cmp	ebx,ecx
			jae	E43
			mov	[ebx],al
			inc	ebx
			inc	LitStringsPtr
			cmp	al,0dh
			jne	GetString
E35:			mov	edi,offset Err35
			jmp	DispError
E43:			mov	edi,offset Err43
			jmp	DispError
@@:			mov	byte ptr [ebx],0
			inc	LitStringsPtr
			mov	WasFloat,VAR_STRING
			pop	ebx
NotAString:		pop	ecx
			ret

; String without quotes (called from INPUT)
			align	8
GetPlainString:		push	ecx
			xor	ecx,ecx
			mov	ebx,LitStringsPtr
			cmp	wtemp3,1
			jne	ErrInString
			xor	ebx,ebx
			mov	WasFloat,VAR_STRING
			pop	ecx
			ret
			align	8
ErrInString:		push	ebx
			mov	ecx,ebx
			add	ecx,MEMSTRINGSIZE
StorePlainString:	mov	al,[edx]
			cmp	al,27
			je	@F
			cmp	al,0dh
			je	@F
			inc	edx
			cmp	ebx,ecx
			jae	E43
			mov	[ebx],al
			inc	ebx
			inc	LitStringsPtr
			test	al,al
			jnz	StorePlainString
@@:			mov	byte ptr [ebx],0
			inc	LitStringsPtr
			mov	WasFloat,VAR_STRING
			pop	ebx
			pop	ecx
			ret

;-----------------------------------------------------------------------------------------------
; Test an ASCII float number from [edx]. 
			align	8
TstNumFloat:		xor	ebx,ebx			; bx = binary result
			call	TstString
			test	ebx,ebx
			jz	ContWithFloat
			ret
ContWithFloat:		push	edx			; Fall back
			mov	ch,bh			; ch=0. Char. counter
			xor	ah,ah			; cancel SkipSpaces comparison
			call	SkipSpaces		; al = first char. found
			push	edi
			mov	edi,FltString
			cmp	wtemp3,1
			jne	SearchFloatingPoint
			mov	byte ptr [edi],"-"	; Add minus sign if reported previously
			inc	edi
			; Conversion loop
SearchFloatingPoint:	cmp	al,'.'
			je	PassStrFloatChar
			cmp	al,'E'
			je	PassStrFloatExpChar
			cmp	al,'e'
			je	PassStrFloatExpChar
			test	ebx,2			; Search a possible exponent
			jz	SearchExponentSign	; sign if value is a floating point
			cmp	al,'+'
			je	NoFloatYet
			cmp	al,'-'
			je	NoFloatYet
SearchExponentSign:	cmp	al,'0'
			jc	R1Flt
			cmp	al,":"
			jnc	R1Flt
			jmp	NoFloatYet
			align	8
PassStrFloatExpChar:	test	ebx,2			; Only one exponent
			jnz	E24
			or	ebx,2			; Found exponent
			jmp	NoFloatYet
			align	8
PassStrFloatChar:	test	ebx,1			; Only one dot
			jnz	E24
			or	ebx,1			; Mark it as floating point
NoFloatYet:		inc	ch			; Char. counter
			mov	[edi],al
			inc	edi
			inc	edx
			mov	al,[edx]
			jmp	SearchFloatingPoint
			align	8
R1Flt:			mov	byte ptr [edi],0	; Append zero
			test	ebx,1
			jz	NoFloatFound
			push	ecx
			invoke	StringToFloat,FltString,addr FltVal
			pop	ecx
			test	eax,eax
			jz	CorrectFloat
E24:			mov	edi,offset Err24
			jmp	DispError
			align	8
CorrectFloat:		pop	edi
			mov	WasFloat,VAR_FLOAT
			add	esp,4			; Don't restore edx
			ret
			align	8
NoFloatFound:		pop	edi
			pop	edx			; Restore position
			mov	WasFloat,VAR_INTEGER
			jmp	TstNum			; And check again with standard routine

;----------------------------------------------------------------------------------------------- 
; DirectCmd execute for Direct commands.
; ExecuteCmdIndirect is here for stack coherency purpose and no direct commands.
			align	8
E34:			mov	edi,offset Err34	; Out of context command issued
			jmp	DispError

;----------------------------------------------------------------------------------------------- 
; ebx = line number
			align	8
DirectCmd:		mov	PreCompLine,0
			mov	ebx,OperStack
			mov	OperStackPtr,ebx
			mov	ebx,DCompExpr
			mov	CompExprPtr,ebx
			mov	ebx,DCompStart
			mov	CompPtr,ebx
			mov	PrecompDirect,1
			push	edx
			call	PreCompCheckMulti
			pop	edx
			mov	edx,DCompStart
			mov	ebx,offset DRunTbl
			add	edx,4			; Pass Line
ExecuteCmdIndirect:	mov	eax,[edx-4]
			mov	LineNumberComp,eax
			movsx	eax,word ptr [edx]	; Get command (+ extend sign)
			add	edx,2
			test	eax,eax
			js	AssignVar
			jmp	dword ptr [ebx+eax*4]	; Jump to command routine
			align	8
AssignVar:		call	LoadVariables		; or variables...
			jmp	NextCmd
			align	8
SearchKeyword:		xor	ah,ah			; No char. for SkipSpaces
			call	SkipSpaces
			push	edi
			push	ecx
			mov	edi,edx
ExecuteCmd:		mov	ecx,[ebx]
			test	ecx,ecx
			jz	@F
			add	ebx,4-1			; Start of keyword-1 (see inc ebx below)
EX1:			mov	al,[edx]		; al = text letter 
			inc	edx			; Text pointer +1
			inc	ebx			; Keywords table pointer +1
			cmp	[ebx],al		; and compare...
			je	EX2			; yes, identicals
			lea	ebx,[ebx+ecx+4]		; Wrap to next entry in the table
			mov	edx,edi			; Restore text
			jmp	ExecuteCmd		; Test next keyword (no need to skip spaces again)
			align	8
EX2:			dec	ecx
			jnz	EX1
			pop	ecx
			inc	ebx			; else, keyword found. ebx points on routine execution
			pop	edi
			jmp	dword ptr [ebx]		; and jump to routine address
			align	8
@@:			pop	ecx
			add	ebx,4+1			; else, keyword found. ebx points on routine execution
			pop	edi
			jmp	dword ptr [ebx]		; jump to last routine address

;-----------------------------------------------------------------------------------------------
			align	8
ExecLine:		cmp	TrState,0
			jne	TrOn
			call	TestInpBuf		; Escape key pressed ?
			cmp	bl,27
			je	Prompt			; Yes, variables are not cancelled and can be printed
TrOn:			mov	ebx,[edx]		; No, Line number in location LineNumber
			mov	LineNumber,ebx
			mov	SaveLineNumber,ebx
ContLine:		add	edx,4			; edx skip binary line number & points on 1st. line char
			mov	ebx,OperStack
			mov	OperStackPtr,ebx
			mov	ebx,offset RunTbl
			jmp	ExecuteCmdIndirect

;-----------------------------------------------------------------------------------------------
; Ucase converts all typed line char. in upper case, except "strings" and REM, from edx to ecx
			align	8
Ucase:			mov	btemp1,0		; Reset quote flag
			xor	eax,eax
UC1:			cmp	ecx,edx			; Reached end of line ?
			jb	UC2			; Yes, return to caller
			mov	al,[edx]
			cmp	al,34			; Quote ?
			jne	@F
			xor	btemp1,1		; Switch btemp1 flag value between 0 and 1
@@:			test	btemp1,1		; String is being read ?
			jnz	NoLetter		; Yes, ignore this char.
			cmp	al,"'"			; Comment ?
			je	UC2			; Yes, nothing more to modify in the line. Return to caller
			cmp	al,"a"			; If char = 'a' to 'z'
			jb	NoLetter
			cmp	al,"z"
			ja	NoLetter
			sub	al,32			; convert it in upper case
			mov	[edx],al
			cmp	al,"R"			; Handle REM command
			jne	Rem1
			or	btemp1,2		; 10
Rem1:			cmp	al,"E"
			jne	Rem2
			or	btemp1,4		; 100
Rem2:			cmp	al,"M"
			jne	NoLetter
			or	btemp1,8		; 1000
NoLetter:		mov	al,btemp1
			and	al,14			; 1110 ? found a REM !
			cmp	al,14			; Stop process
			je	UC2			; if found
			inc	edx
			jmp	UC1
			align	8
UC2:			ret

; Save memory block into a file
			align	8
SaveFile		proc	FName:dword,MemPos:dword,MemEnd:dword
			local	FHandle:dword
			local	FRealRead:dword
			local	FValid:dword
			local	FSize:dword
			pushad
			mov	ebx,MemEnd
			sub	ebx,MemPos
			mov	FSize,ebx
			mov	FValid,0
			invoke	DeleteFile,FName		; Delete it before
			invoke	CreateFile,FName,GENERIC_WRITE,0,0,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE or FILE_FLAG_RANDOM_ACCESS,0
			inc	eax
			je	Error_SaveFile
			dec	eax
			mov	FHandle,eax
			invoke	WriteFile,FHandle,MemPos,FSize,addr FRealRead,0
			and	eax,1				; Backward compatibility (change dis)
			mov	FValid,eax
			invoke	CloseHandle,FHandle
Error_SaveFile:		mov	eax,FValid
			popad
			ret
SaveFile		endp

;-----------------------------------------------------------------------------------------------
; Pre-compile a program prior to an execution (or full compilation).
; (Just replace commands with their numbers and length)
; Output encoding:
; 1.l: line number
; 1.w: command number
; 1.l: pointer to parameters block
; 1.b: 0dh char
; Lines containing several commands are "unfolded".
; Comments are completly removed.
; Variables assignments are passed "as is" with -1 command
; Spaces are NOT stripped (we need them for the interpreter).
; IF command is a special case:
; we need to obtain the expression following it and then the possible command.
			align	8
PreComp:		mov	CompPtr,ebx
			mov	CompExprPtr,ecx
			xor	ebx,ebx
			call	FindLine		; Point in first line
PreLine:		jnc	@F			; If c=1, end of text, else print next line
			mov	ebx,CompPtr
			mov	CompEnd,ebx
			mov	ebx,CompExprPtr
			mov	CompExprEnd,ebx
			ret
			align	8
@@:			push	edi
			push	ecx
			call	PreCompSingleLine
			pop	ecx
			pop	edi
			xor	ebx,ebx
			call	LineNumCompare		; Find next line to print
			jmp	PreLine			; and loop back
			align	8
; Pre-compile a single line
PreCompSingleLine:	mov	edi,[edx]
			mov	PreCompLine,edi
			add	edx,4			; Pass line number now
PreCompCheckMulti:	mov	PreCompNbr,0
			lea	edi,PreCompTbl
			; Search the command in the list
			mov	ah,0dh			; Check empty line
			call	SkipSpaces		; + skip leading spaces
			jz	PreCompNext
PreCompAgain:		mov	ebx,[edi]		; Get address of command
			mov	ecx,[ebx]		; Len (min=1)
			mov	PreCompLen,cx
			add	ebx,4			; Go to keyword
			push	esi
			mov	esi,edx			; Save position of text
PreCompSearch:		mov	al,[edx]
			cmp	[ebx],al
			je	PreCompCheckChar
			; Not found: next command
			add	edi,4
			inc	PreCompNbr
			; Command can't be found in the whole table
			; Write it as a variable assignment
			; the interpreter will check for the validity of the
			; statement itself.
			cmp	PreCompNbr,NBRPRECOMPTBL
			jne	MaxPreComp
			mov	PreCompNbr,-1		; -1 = var assignment
			mov	edx,esi			; Start of line
			; (don't pass over any command in this case)
			jmp	CompVar
			align	8
MaxPreComp:		mov	edx,esi
			pop	esi
			jmp	PreCompAgain
			align	8
PreCompCheckChar:	inc	ebx
			inc	edx
			dec	ecx
			jne	PreCompSearch
			; Ensure that a space or enter char follows the found command
			cmp	byte ptr [edx]," "
			je	PreCompTrailingSpc
			cmp	byte ptr [edx],0dh	; Empty...
			je	PreCompTrailingSpc
			cmp	byte ptr [edx],":"	; Next command
			je	PreCompTrailingSpc
			cmp	PreCompNbr,0		; Comments don't need
			je	PreCompTrailingSpc	; trailing spaces
			mov	eax,PreCompLine
			mov	LineNumber,eax		; Register the line
			jmp	E0
			align	8
PreCompTrailingSpc:	; Found it: backup'n'modify the line in back buffer
			mov	edx,esi			; Start of line
			movzx	eax,PreCompLen		; Pass over command
			add	edx,eax
CompVar:		cmp	PreCompNbr,COMPREM
			je	PreCompStripREM
			cmp	PreCompNbr,0		; Handle comments
			jne	PassCompREM
PreCompStripREM:	mov	eax,PreCompLine
			mov	ebx,CompPtr
			mov	[ebx],eax		; Write line number
			mov	ax,PreCompLen
			add	ebx,4
			mov	ax,PreCompNbr
			mov	[ebx],ax		; Write command number
			add	ebx,2
			mov	dword ptr [ebx],0	; No args
			add	ebx,4
WrapCommentsComp:	mov	al,[edx]
			cmp	al,0dh
			jz	@F
			inc	edx
			jmp	WrapCommentsComp
@@:			inc	edx
			jmp	WrapComments
			align	8
PassCompREM:		mov	eax,PreCompLine
			mov	LineNumber,eax
			mov	eax,OperStack		; Reset expressions stack here
			mov	OperStackPtr,eax	; (Local to one command)
			; LET is handled as a standard variable assignment
			cmp	PreCompNbr,COMPLET	; Same as var assignment
			je	WrapVarLet
			cmp	PreCompNbr,-1		; -1 = var assignment
			jne	PassCompVar
WrapVarLet:		push	ecx
			push	esi
			push	edi
			mov	edi,CompExprPtr
			mov	SaveCompExprPtr,edi
			mov	ExecMode,EXECMODE_PRECOMP
			call	VarEquExpr
			mov	ExecMode,EXECMODE_INTERPRET
			pop	edi
			pop	esi
			pop	ecx
			mov	eax,PreCompLine
			mov	ebx,CompPtr
			mov	[ebx],eax		; Write line number
			mov	ax,PreCompLen
			add	ebx,4
			mov	ax,PreCompNbr
			mov	[ebx],ax		; Write command number
			add	ebx,2
			mov	eax,SaveCompExprPtr
			mov	[ebx],eax		; Save address of decoded var loading
			add	ebx,4
			jmp	CompCmdTerminator
PassCompVar:		push	ecx
			push	esi
			push	edi
			mov	edi,CompExprPtr
			mov	SaveCompExprPtr,edi
			movzx	eax,word ptr PreCompNbr
			mov	ExecMode,EXECMODE_PRECOMP
			call	[RunTblComp+eax*4]
			mov	ExecMode,EXECMODE_INTERPRET
			pop	edi
			pop	esi
			pop	ecx
PassOtherCmd:		mov	eax,PreCompLine
			mov	ebx,CompPtr
			mov	[ebx],eax		; Write line number
			mov	ax,PreCompLen
			add	ebx,4
			mov	ax,PreCompNbr
			mov	[ebx],ax		; Write command number
			add	ebx,2
			; Write a pointer to calculated expression
			mov	eax,SaveCompExprPtr
			mov	[ebx],eax		; Save address of decoded var loading
			add	ebx,4
CompCmdTerminator:	mov	byte ptr [ebx],0dh	; End of IF
			inc	ebx
			mov	CompPtr,ebx
			pop	esi
			cmp	byte ptr [edx],":"	; Another command following ?
			jne	PassLineSeparator
			inc	edx
PassLineSeparator:	jmp	PreCompCheckMulti
			; Store the line terminator first
WrapComments:		mov	byte ptr [ebx],0dh
			inc	ebx
			mov	CompPtr,ebx
			mov	ah,0dh
			call	SkipSpaces
PreCompComment:		pop	esi
PreCompNext:		ret

;-----------------------------------------------------------------------------------------------
; Save FOR loop parameters in ForNextStack
			align	8
ForPushStack:		mov	eax,ForStackPtr
			cmp	eax,ForNextStack		; ForStackPtr out of ForNextStack area limit ?
			ja	@F				; No, OK
			mov	edi,offset Err14
			jmp	DispError			; Yes, error <More than 16 FOR..NEXT levels>
			align	8
@@:			xchg	eax,esp				; esp at the top of stack area
			push	ForAdrVar			; Save 5 parameters of FOR..NEXT loop
			push	ForPtrTxt
			push	ForLine				; backward line
			push	ForLimit			; Max
			push	ForIncrement			; Step
			xchg	eax,esp				; Restore esp
			mov	ForStackPtr,eax			; update FOR..NEXT stack pointer
			ret

;----------------------------------------------------------------------------------------------
; LoadVariables test end of line.
; If found execute next line, else execute a variable assignation
			align	8
LoadVariables:		call	CanExecute
			jz	NoThenUnVerified
			; Can be a variable assignation of an array assignation
			mov	eax,[edx]			; Get address
			add	edx,4
			mov	bl,[eax]			; Get command
			cmp	bl,WRAP_VARADDRESS		; Can't assign to an address
			je	E39
			cmp	bl,WRAP_ARRAYADDRESS
			je	E39
			cmp	bl,WRAP_VAR
			jne	GetVarAssign
GetVarAddrAssign:	inc	eax
			mov	ebx,[eax]			; Get variable offset
			;shl	ebx,4
			add	eax,4				; Point to first data of loading expr
			add	ebx,AZVar			; Calculate address
LoadValue:		push	ebx				; Save it
			push	edx
			mov	edx,eax
			push	ecx
			call	EvalExprCOMP			; Evaluate
			mov	eax,edx
			pop	ecx
			pop	edx
			pop	ecx
			cmp	ExprFinalResult,VAR_STRING
			je	StoreStringVar
			cmp	ExprFinalResult,VAR_FLOAT
			je	StoreFloatVar
			mov	[ecx],ebx
			mov	dword ptr [ecx+8],VAR_INTEGER
			jmp	@F
StoreFloatVar:		fstp	qword ptr [ecx]			; Store floating point
			mov	dword ptr [ecx+8],VAR_FLOAT
			jmp	@F
StoreStringVar:		mov	[ecx],ebx
			mov	dword ptr [ecx+8],VAR_STRING
			jmp	@F
GetVarAssign:		cmp	bl,WRAP_ARRAY			; An array...
			jne	@F
			inc	eax
			push	edx
			mov	edx,eax
			call	GetArrayArgs
			mov	eax,edx
			pop	edx
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			shl	ebx,4				; Get real index
			add	ebx,ArrayVar
			jmp	LoadValue
@@:			ret
E39:			mov	edi,offset Err39	; Error: <Unexpected address>
			jmp	DispError

;----------------------------------------------------------------------------------------------
; EvalExpr evaluates arithmetic or relational expressions.
; <EXPR> = <EXPR2> or <EXPR2><REL.OP><EXPR2>
; Where <REL.OP> is one of the operators in TAB_REL_OP and the result of these operations is
; 1 if True or 0 if False
; <EXPR2> = (+ or -) <EXPR3> (+ or -) <EXPR3> (....)
; Where () are optional and (....) are optional repeats.
; <EXPR3> = <EXPR2> (* or / or \) <EXPR2>) (....)
; <TERM> = (+ or -) <VARIABLE> or (+ or -) <FUNCTION> or (+ or -) <EXPR>
;
; <EXPR> is recursive so that - variable '@' can have an EXPR as index,
;                             - FUNCTIONS can have an <EXPR> as arguments, and
;                             - <TERM> can be an <EXPR> in parentheses
; Note: floating points have priority over integers: if there's any floating point
; in a complete expression of same level then the whole expression 
; will be calculated with fpu.
; ExprFinalResult variable contains the last known type of an expression.

; EXPR
			align	8
EvalExpr:		call	EXPR2			; <EXPR2> = (+ or -) <EXPR3> (+ or -) <EXPR3> (....)
			push	ebx
			mov	ebx,offset TAB_REL_OP	; Lookup relational operator table
			jmp	SearchKeyword		; Go do it. If not found, go to X17
			align	8
Op_GTEQ:		invoke	AddWrapOperator,WRAP_GREATEREQU
			call	DoRelOp			; Rel.Op '>='
			jc	@F			; False, bl = 0
			mov	bl,al			; True,  bl = 1
@@:			jmp	EndExpr
			align	8
Op_NOTEQ:		invoke	AddWrapOperator,WRAP_NOTEQU
			call	DoRelOp			; Rel.Op '<>'
			jz	@F			; False, bl = 0
			mov	bl,al			; True,  bl = 1
@@:			jmp	EndExpr
			align	8
Op_GT:			invoke	AddWrapOperator,WRAP_GREATER
			call	DoRelOp			; Rel.Op '>'
			jz	@F			; False, bl = 0
			jc	@F			; Also False, bl = 0
			mov	bl,al			; True,  bl = 1
@@:			jmp	EndExpr
			align	8
Op_LESSEQ:		invoke	AddWrapOperator,WRAP_LOWEREQU
			call	DoRelOp			; Rel.Op '<='
			mov	bl,al			; Set bl = 1
			jz	@F			; True,  bl = 1
			jc	@F			; Also True,  bl = 1
			mov	bl,bh			; Else False, bl = 0
@@:			jmp	EndExpr
			align	8
Op_EQ:			invoke	AddWrapOperator,WRAP_EQU
			call	DoRelOp			; Rel.Op '='
			jnz	@F			; False, bl = 0
			mov	bl,al			; True,  bl = 1
@@:			jmp	EndExpr
			align	8
Op_LESS:		invoke	AddWrapOperator,WRAP_LOWER
			call	DoRelOp			; Rel.Op '<'
			jnc	@F			; False, bl = 0
			mov	bl,al			; True,  bl = 1
@@:			jmp	EndExpr
			align	8
Op_Unknown:		pop	ebx			; No Rel. Op found
EndExpr:		call	PopOperStack
			mov	ExprFinalResult,eax	; Store evaluated type for commands
			invoke	AddWrapExprEnd		; Mark expression as finished
			ret				; Return ebx = <EXPR2>
			align	8
DoRelOp:		mov	al,cl			; Subroutine for all Rel. Op's.
			pop	ebx			; Reverse top of stack
			pop	ecx
			push	ebx
			push	ecx
			mov	cl,al
			call	EXPR2			; Get 2nd. expression <EXPR2>
			xchg	edx,ebx			; Value in edx now
			pop	eax
			push	ebx
			call	LoadOperTerms
			test	bl,bl
			jz	NormCompare
			; Comps always returns an integer
			invoke	PushOperStack,VAR_INTEGER
			pop	edx
			mov	ebx,0			; Use movs here because
			mov	al,1			; we need to retrieve flags later on
			ret
			align	8
NormCompare:		mov	ebx,eax
			invoke	PushOperStack,VAR_INTEGER
			pop	edx
			mov	ebx,0
			mov	al,1
			ret
			align	8
EXPR2:			;<EXPR2> = (+ or -) <EXPR3> (+ or -) <EXPR3> (....)
			mov	ah,'-'
			call	SkipSpaces		; Negative sign before EXPR3 ?
			jnz	X21			; No
			xor	ebx,ebx			; Yes, fake '0-'
			; Note: if the following term is floating point
			; the ebx value (0) will be converted and used instead.
			; so we don't have to use fldz here.
			; (faked push for negative numbers)
			invoke	PushOperStack,VAR_INTEGER
			invoke	AddWrapOperator,WRAP_MINUS
			jmp	X27			; Treat like substract
			align	8
X21:			mov	ah,'+'			; Positive sign before EXPR3 ?
			call	SkipSpaces		; '+' or nothing is same
			call	EXPR3			; 1st <EXPR3> = <TERM> (<* or />) <TERM>) (....)
X23:			mov	ah,'+'
			call	SkipSpaces		; Add a new EXPR3 ?
			jnz	X26			; No, test '-'
			invoke	AddWrapOperator,WRAP_PLUS
			push	ebx			; Yes, save value
			call	EXPR3			;     Get 2nd <EXPR3>
X24:			xchg	edx,ebx			;     Result in edx
			pop	eax
			push	ebx
			call	LoadOperTerms
			invoke	PushOperStack,VAR_INTEGER
			pop	edx
			jmp	X23			; Look for more
X26:			mov	ah,'-'
			call	SkipSpaces		; Substract a new EXPR3 ?
			jnz	R4			; No, Ret EXPR2
			invoke	AddWrapOperator,WRAP_MINUS
X27:			push	ebx			; Yes, save previous <EXPR3>
			call	EXPR3			;     get 2nd <EXPR3> (= <TERM> (<* or />) <TERM>) (....))
			call	PeekOperStack
			cmp	eax,VAR_FLOAT
			je	ChangeFltSign
			call	ChangeSign		;     change sign
			jmp	X24			;     and looks for a next '+' or '-'
			align	8
ChangeFltSign:		call	ChangeSignFlt
			jmp	X24
			align	8
EXPR3:			;<EXPR3> = <TERM> (* or / or \) <TERM>) (....)
			call	Expr_GetTerm		; Get 1st. <TERM>
; Performs muls
Op_Mul:			mov	ah,'*'
			call	SkipSpaces		; Multiply ?
			jnz	Op_Div			; No, test divide
			invoke	AddWrapOperator,WRAP_MUL
			push	ebx			; Yes, save 1st.
			call	Expr_GetTerm		; and get 2nd <TERM>
			xchg	edx,ebx			; 2nd in edx now
			pop	eax
			push	ebx
			call	LoadOperTerms
			invoke	PushOperStack,VAR_INTEGER
			jmp	X35			; Look for more
			align	8
Op_Div:			mov	ah,'/'
			call	SkipSpaces		; Divide ?
			jnz	Op_DivFlt		; No, ret 
			cmp	ExecMode,EXECMODE_PRECOMP
			jne	StoreDiv
			invoke	AddWrapOperator,WRAP_DIV
StoreDiv:		push	ebx			; Yes, save 1st <TERM>
			call	Expr_GetTerm		; and get the second one
			xchg	edx,ebx			; Put 2nd in edx
			pop	eax
			push	ebx
			call	LoadOperTermsFlt	; Force terms to fp
			invoke	PushOperStack,VAR_FLOAT	; Last term was fp
			jmp	X35			; Look for more
			align	8
NormDiv:		invoke	PushOperStack,VAR_INTEGER	
X35:			pop	edx			; Restore text pointer
			jmp	Op_Mul			; Look for more terms
			align	8
ExprDivZero:		mov	edi,offset Err6		; Yes, Error : Division by zero
			jmp	DispError

; Performs integer divs
			align	8
Op_DivFlt:		mov	ah,'\'
			call	SkipSpaces		; Divide ?
			jnz	R4			; No, ret 
			invoke	AddWrapOperator,WRAP_INTDIV
			push	ebx			; Yes, save 1st <TERM>
			call	Expr_GetTerm		; and get the second one
			xchg	edx,ebx			; Put 2nd in edx
			pop	eax
			push	ebx
			call	LoadOperTerms		; load them into fpu
			jmp	NormDiv			

;----------------------------------------------------------------------------------------------
; Get real term sign
			align	8
CheckTermSign:		xor	ebx,ebx
CheckNegTerm:		mov	ah,'-'
			call	SkipSpaces
			jnz	NegTerm
			xor	ebx,1
			jmp	CheckNegTerm
NegTerm:		mov	ah,'+'
			call	SkipSpaces
			jnz	NegatedTerm
			jmp	CheckNegTerm
NegatedTerm:		ret

;----------------------------------------------------------------------------------------------
; Unary terms retrieving
; <TERM> = (+ or -) <VARIABLE> or (+ or -) <FUNCTION> or (+ or -) <EXPR>
			align	8
Expr_GetTerm:		call	CheckTermSign
			test	ebx,ebx
			jz	NoMarkNegTerm
			invoke	AddWrapMinusSign
NoMarkNegTerm:		push	ebx
			mov	ebx,PtrTblFnc
			movzx	eax,al
			mov	ebx,[ebx+eax*4]		; Find the function
			jmp	SearchKeyword		; And got do it
; Functions terminator (called from each function)
; used to change the sign of the resulting value if needed.
			align	8
FinalizeFnc:		pop	eax
			test	eax,eax
			jz	NoNegIntFnc
			neg	ebx
NoNegIntFnc:		ret
			align	8
Expr_GetVar:		call	TestVar			; A variable ?
			jc	Expr_GetNumber		; Not a variable
			invoke	PushOperStack,VAR_INTEGER
			pop	ebx
			ret
			align	8
ExprString:		invoke	PushOperStack,VAR_STRING
			pop	ebx
			ret
			align	8
ExprFloat:		; Load floating point value from variable
			; Push it into fpu stack
			invoke	PushOperStack,VAR_FLOAT
SendTermToValHalla:	pop	ebx
			ret
			align	8
Expr_GetNumber:		mov	wtemp3,0		; Don't test local sign here
			call	TstNumFloat		; Or is it a number ?
			cmp	WasFloat,VAR_STRING	; Was it a string ?
			je	StringInExpr
			cmp	WasFloat,VAR_INTEGER
			je	NoFloatInExpr
			mov	al,ch			; Number of digits
			test	al,al
			jz	NoFloatInExpr		; no digits found ?
			invoke	PushOperStack,VAR_FLOAT
			fld	qword ptr [FltVal]	; Load floating point value
			; Push it into fpu stack
CheckParentFlt:		pop	ebx
			test	ebx,ebx
			jz	NoNegFltLit
			fchs
NoNegFltLit:		fstp	qword ptr [FltVal]
			invoke	AddWrapFp,FltVal
			ret
			align	8
StringInExpr:		invoke	PushOperStack,VAR_STRING
CheckParentStr:		pop	eax
			test	eax,eax
			jz	NoNegStrLit
			jmp	E26			; Can't negate a string
NoNegStrLit:		invoke	AddWrapString,ebx
R4:			ret
			align	8
NoFloatInExpr:		mov	al,ch			; Number of digits
			test	al,al
			jnz	NoTestParenth		; A recognized number ?
			call	TestParenth		; (Should return the last known type)
			jmp	SendTermToValHalla
			align	8
NoTestParenth:		invoke	PushOperStack,VAR_INTEGER
RetrieveIntFromParentH:	pop	eax
			test	eax,eax
			jz	NoNegIntLit
			neg	ebx
NoNegIntLit:		invoke	AddWrapInteger,ebx
			ret

			align	8
TestParenthOpen:	mov	ah,"("
			call	SkipSpaces		; Not a number, must be an (expr)
			jnz	CompletNullParentOpen	; Not a "("
			invoke	AddWrapOpenBracket
			cmp	byte ptr [edx],")"
			je	EmptyParenthOpen
			call	EvalExpr		; "(" found, evaluate EXPR
			; Load last expression type as current one
			invoke	PushOperStack,ExprFinalResult
			ret
			align	8
CompletNullParentOpen:	mov	ExprFinalResult,VAR_NOFNCARG
			invoke	PushOperStack,ExprFinalResult
			ret
			align	8
EmptyParenthOpen:	mov	ExprFinalResult,VAR_NOFNCARG
			invoke	PushOperStack,ExprFinalResult
@@:			ret

			align	8
TestParenth:		mov	ah,"("
			call	SkipSpaces		; Not a number, must be an (expr)
			jnz	CompletNullParent	; Not a "("
			invoke	AddWrapOpenBracket
			cmp	byte ptr [edx],")"
			je	EmptyParenth
			call	EvalExpr		; "(" found, evaluate EXPR
			invoke	AddWrapCloseBracket
			; Load last expression type as current one
			invoke	PushOperStack,ExprFinalResult
			jmp	@F
			align	8
CompletNullParent:	mov	ExprFinalResult,VAR_NOFNCARG
			invoke	PushOperStack,ExprFinalResult
			ret
			align	8
EmptyParenth:		mov	ExprFinalResult,VAR_NOFNCARG
			invoke	PushOperStack,ExprFinalResult
			invoke	AddWrapCloseBracket
@@:			mov	ah,")"			; followed by a ")"
			call	SkipSpaces		; OK, ret
			jz	NoBrackH
E0:			mov	edi,offset Err0		; else error <Syntax error>
			jmp	DispError
			align	8
NoBrackH:		ret

;----------------------------------------------------------------------------------------------
; Check operation terms types and load them in FltTerm1/FltTerm2 
; if they are floating points
; Retrieve 2 terms for an operation
; out: bl=1 if fp operation
;      bl=0 if integer operation
			align	8
LoadOperTerms:		push	esi
			push	eax
			call	PopOperStack
			mov	ebx,eax
			call	PopOperStack
			mov	esi,ebx			; check if the operation terms
			or	esi,eax			; contains at least one fp
			xchg	bh,bl			; bh=2nd term (last)
			mov	bl,al			; bl=1th term (first)
			pop	eax
			test	esi,VAR_FLOAT		; Yes: everything must be converted to fp
			jz	NormTerm
			; Term 2 was a float (should be in st(0) then)
ForcedFltTerms:		cmp	bh,VAR_FLOAT
			jne	LoadTerm2Flt
			fstp	FltTerm2		; get it
			jmp	LoadTerm2
			align	8
LoadTerm2Flt:		cmp	bh,VAR_INTEGER
			jne	LoadTerm2
			mov	FpIntLoad,edx
			fild	FpIntLoad		; Load it as integer
			fstp	FltTerm2		; save it as fp
LoadTerm2:		cmp	bl,VAR_FLOAT
			jne	LoadTerm1Flt
			fstp	FltTerm1		; retrieve it from the stack
			jmp	LoadTerm1
			align	8
LoadTerm1Flt:		cmp	bl,VAR_INTEGER
			jne	LoadTerm1
			mov	FpIntLoad,eax
			fild	FpIntLoad
			fstp	FltTerm1
LoadTerm1:		mov	bl,1
			pop	esi
			ret
			align	8
NormTerm:		xor	bl,bl
			pop	esi
			ret
; Load terms into fpu
			align	8
LoadOperTermsFlt:	push	esi
			push	eax
			call	PopOperStack
			mov	ebx,eax
			call	PopOperStack
			xchg	bh,bl			; bh=2nd term (last)
			mov	bl,al			; bl=1th term (first)
			pop	eax
			jmp	ForcedFltTerms

;----------------------------------------------------------------------------------------------
; Manages incremental stack for operations terms types
			align	8
PushOperStack		proc	StackValue:dword
			push	eax
			push	ebx
			push	ecx
			mov	eax,StackValue
			lea	ecx,OperStackPtr
			mov	ebx,[ecx]
			mov	[ebx],eax			; Write type
			add	dword ptr [ecx],4		; Increment
			mov	ebx,[ecx]
			sub	ebx,OperStack
			cmp	ebx,256				; 256 terms = max
			jl	MaxOperStack
			mov	edi,offset Err25
			jmp	DispError
			align	8
MaxOperStack:		pop	ecx
			pop	ebx
			pop	eax
			ret
PushOperStack		endp

; Return term type in eax
; and pop the stack
			align	8
PopOperStack:		push	ebx
			push	ecx
			lea	ecx,OperStackPtr
			sub	dword ptr [ecx],4		; Decrement
			mov	ebx,[ecx]
			sub	ebx,OperStack
			jge	MinOperStack
E26:			mov	edi,offset Err26
			jmp	DispError
			align	8
MinOperStack:		mov	ecx,[ecx]
			mov	eax,[ecx]
			pop	ecx
			pop	ebx
			ret

; Return term type in eax
			align	8
PeekOperStack:		push	ebx
			push	ecx
			lea	ecx,OperStackPtr
			mov	ebx,[ecx]
			sub	ebx,OperStack
			jg	MinPeekOperStack
			jmp	E26
			align	8
MinPeekOperStack:	mov	ecx,[ecx]
			mov	eax,[ecx-4]		; Peek from previous value
			pop	ecx
			pop	ebx
			ret

; Set type of the current entry
			align	8
PokeOperStack		proc	StackValue:dword
			push	eax
			push	ebx
			push	ecx
			lea	ecx,OperStackPtr
			mov	ebx,[ecx]
			sub	ebx,OperStack
			jg	MinPokeOperStack
			jmp	E26
			align	8
MinPokeOperStack:	mov	ecx,[ecx]
			mov	eax,StackValue
			mov	[ecx-4],eax		; Poke in previous value
			pop	ecx
			pop	ebx
			pop	eax
			ret
PokeOperStack		endp

;----------------------------------------------------------------------------------------------
; Manages incremental stack for if commands
			align	8
PushIfStack		proc	StackValue:dword
			push	eax
			push	ebx
			push	ecx
			push	edi
			push	edx
			mov	eax,StackValue
			lea	ecx,IfStackPtr
			mov	ebx,[ecx]
			mov	edi,LineNumber
			cmp	[ebx-4],edi			; Don't push it twice
			je	RefreshIfStack
			mov	[ebx],eax			; Condition result
			mov	[ebx+4],edi			; Save line number
			add	dword ptr [ecx],8		; Increment
			mov	edx,[ecx]
			sub	edx,IfStack
			cmp	edx,256				; 256 levels = max
			jl	MaxIfStack
			mov	edi,offset Err31
			jmp	DispError
			align	8
RefreshIfStack:		; Just refresh IF status
			; If an entry for the same line is already set
			mov	[ebx-8],eax
MaxIfStack:		pop	edx
			pop	edi
			pop	ecx
			pop	ebx
			pop	eax
			ret
PushIfStack		endp

; Return condition result in eax
; and pop the stack
			align	8
PopIfStack:		push	ebx
			push	ecx
			lea	ecx,IfStackPtr
			sub	dword ptr [ecx],8		; Decrement
			mov	ebx,[ecx]
			mov	dword ptr [ebx],0		; Erase it
			mov	dword ptr [ebx+4],-1
			sub	ebx,IfStack
			jge	MinIfStack
E32:			mov	edi,offset Err32
			jmp	DispError
			align	8
MinIfStack:		mov	ecx,[ecx]
			mov	eax,[ecx]
			pop	ecx
			pop	ebx
			ret

; Return condition result type in eax
			align	8
PeekIfStack:		push	ebx
			push	ecx
			lea	ecx,IfStackPtr
			mov	ebx,[ecx]
			sub	ebx,IfStack
			jg	MinPeekIfStack
			mov	edi,offset Err32
			jmp	DispError
			align	8
MinPeekIfStack:		mov	ecx,[ecx]
			mov	eax,[ecx-8]		; Peek from previous value
			test	eax,eax
			pop	ecx
			pop	ebx
			ret

; Set condition result of the current entry
			align	8
PokeIfStack		proc	StackValue:dword
			push	eax
			push	ebx
			push	ecx
			lea	ecx,IfStackPtr
			mov	ebx,[ecx]
			sub	ebx,IfStack
			jg	MinPokeIfStack
			mov	edi,offset Err32
			jmp	DispError
			align	8
MinPokeIfStack:		mov	ecx,[ecx]
			mov	eax,StackValue
			mov	[ecx-8],eax		; Poke in previous value
			pop	ecx
			pop	ebx
			pop	eax
			ret
PokeIfStack		endp

; Check if program is running in IF mode by checking stack level
; Returns 0 if in IF mode, 1 otherwise.
			align	8
CheckIFMode:		push	ebx
			push	ecx
			lea	ecx,IfStackPtr
			mov	ebx,[ecx]
			sub	ebx,IfStack
			jg	InIFMode
			pop	ecx
			pop	ebx
			xor	eax,eax
			inc	eax			; 1
			ret
			align	8
InIFMode:		pop	ecx
			pop	ebx
			xor	eax,eax			; 0
			ret

; Check if program is running in IF mode and under a verified condition
; Returns 1 if ok, 0 otherwise.
; CheckIfMode (see above) returns 1 to simulate a verified condition.
			align	8
CanExecute:		call	CheckIFMode
			jnz	ValidExecute
			call	PeekIfStack		; Returns condition type
			jns	ValidExecute		; Ghosted IF (-1)
			xor	eax,eax
ValidExecute:		ret

;----------------------------------------------------------------------------------------------
; Look for any pending if statements
			align	8
CheckPendingIf:		call	CheckIFMode			; Pending IFs ?
			jnz	UnexpectedEOF
			mov	edi,offset Err33		; else, error <Unexpected end of file>
			jmp	DispError
			align	8
UnexpectedEOF:		ret

;----------------------------------------------------------------------------------------------
; Append .MBI extension to a filename if necessary
			align	8
AppendMBasicExtension:	; Check if .MBI extension is present
			push	edx
			invoke	lstrlen,FileName
			pop	edx
			cmp	eax,4			; Check if it is equal of smaller than extension
			jg	MinFileExt		; If yes then we can append it safely
			push	edx
			invoke	lstrcat,FileName,addr FileExt
			pop	edx
			jmp	ExtAppened
			align	8
MinFileExt:		mov	ebx,FileName
			mov	ebx,[ebx+eax-4]
			and	ebx,0dfdfdfffh		; Lower case
			cmp	ebx,"HSB."		; Extension already present ?
			je	ExtAppened		; Yes: no need to append it twice
			push	edx
			invoke	lstrcat,FileName,addr FileExt
			pop	edx
ExtAppened:		ret

;----------------------------------------------------------------------------------------------
; (The following fp procedures were written by Tim Roberts so thanks to him).
; Multiply a floating point value by an integral power of 10.
; Entry: EAX = power of 10, -4932..4932.
;	ST(0) = value to be multiplied
; Exit:	ST(0) = value x 10^eax
			align	8
PowOf10			proc
			mov	ecx,eax
			.if (sdword ptr eax < 0)
				neg	eax
			.endif
			fld1
			mov	dl,al
			and	edx,0fh
			.if (!zero?)
				lea	edx,[edx+edx*4]
				fld	ten_1[edx*2][-10]
				fmulp	st(1),st
			.endif
			mov	dl,al
			shr	dl,4
			and	edx,0fh
			.if (!zero?)
				lea	edx,[edx+edx*4]
				fld	ten_16[edx*2][-10]
				fmulp	st(1),st
			.endif
			mov	dl,ah
			and	edx,1fh
			.if	(!zero?)
				lea	edx,[edx+edx*4]
				fld	ten_256[edx*2][-10]
				fmulp	st(1),st
			.endif
			.if (sdword ptr ecx < 0)
				fdivp	st(1),st
			.else
				fmulp	st(1),st
			.endif
			ret
PowOf10			endp

;----------------------------------------------------------------------------------------------
; Convert a string to a double-precision floating point number
; returns 0 if correct -1 otherwise
			align	8
StringToFloat		proc	szIn:ptr byte,fpout:ptr qword
			local	sign:byte
			local	expsign:byte
			local	decimal:dword
			local	stat:word
			local	temp:word
			push	ebx
			push	ecx
			push	edx
			push	esi
			push	edi
			xor	eax,eax
			mov	[sign],al
			mov	[expsign],al
			mov	[decimal],-1
			fstcw	[stat]
			mov	[temp],027fh
			fldcw	[temp]
; First, see if we have a sign at the front end.
			mov	esi,[szIn]
			mov	al,[esi]
			.if (al == '+')
				inc	esi
				mov	al,[esi]
			.elseif (al == '-')
				inc	esi
				mov	[sign],1
				mov	al,[esi]
			.endif
			test	al,al		; null string?
			jz	sdExit
; Initialize the floating point unit.
			fclex
			xor	ebx,ebx
			fldz
			xor	ecx,ecx
; OK, now start our main loop.
;   esi => character in string now in al
;   al = next character to be converted
;   ebx = number of digits encountered thus far
;   ecx = exponent
;   ST(0) = accumulator
cvtloop:		cmp	al,'E'
			je	doExponent
			cmp	al,'e'
			je	doExponent
			.if	(al == '.')
				mov	[decimal],ebx	; remember decimal point location
			.else
				sub	al,'0'		; convert ASCII to BCD
				jb	sdFinish	; if not a digit
				cmp	al,9
				ja	sdFinish	; if not a digit
				mov	[temp],ax
				fmul	[ten]		; d *= 10
				fiadd	[temp]		; d += new digit
				inc	ebx		; increment digit counter
			.endif
			inc	esi
			mov	al,[esi]
			jnz	cvtloop
			jmp	sdFinish
; We have the mantissa at the top of the stack.  Now convert the exponent.
; Fortunately, this is an integer.
;   esi = pointer to character in al
;   al = next character to convert
;   ebx = digit counter
;   ecx = accumulated exponent
;   ST(0) = mantissa
			align	8
doExponent:		inc	esi
			mov	al,[esi]
			test	al,al
			jz	sdFinish
			; Does the exponent have a sign?
			.if (al == '+')
				inc	esi
				mov	al,[esi]
			.elseif (al == '-')
				inc	esi
				mov	[expsign],1
				mov	al,[esi]
			.endif
			test	al,al
			jz	sdFinish
expLoop:		sub	al,'0'
			jb	sdFinish
			cmp	al,9
			ja	sdFinish
			imul	ecx,10
			add	ecx,eax
			inc	esi
			mov	al,[esi]
			jnz	expLoop
; Adjust the exponent to account for decimal places.  At this juncture, 
; we work with the absolute value of the exponent.  That means we need
; to subtract the adjustment if the exponent will be negative, add if
; the exponent will be positive.
;  ST(0) = mantissa
;  ecx = unadjusted exponent
;  ebx = total number of digits
sdFinish:		.if ([expsign] != 0)
				neg	ecx
			.endif
			mov	eax,[decimal]
			.if (eax != -1)
				sub	ebx,eax	; ebx = digits to right of decimal place
				sub	ecx,ebx	; adjust exponent
			.endif
			; Multiply by 10^exponent.
			;  ST(0) = mantissa
			;  ecx = exponent
			mov	eax,ecx
			call	PowOf10
			; Negate the whole thing, if necessary.
			.if ([sign] != 0)
				fchs
			.endif
			; That's it! Store it and go home.
			mov	edi,[fpout]
			fstp	qword ptr [edi]	; store the result
			fwait
sdExit:			fldcw	[stat]
			or	eax,-1
			mov	bl,[esi]	; Get last unread char
			test	bl,bl		; If char is null
			jnz	NoticeFloat	; then it is a correct number
			xor	eax,eax		; We report zero then
NoticeFloat:		pop	edi
			pop	esi
			pop	edx
			pop	ecx
			pop	ebx
			ret
StringToFloat		endp

;----------------------------------------------------------------------------------------------
; Convert a floating point register to ASCII.  For internal use.
; The result always has exactly 18 digits, with zero padding on the
; left if required.
; Entry:	ST(0) = a number to convert, 0 <= ST(0) < 1E19.
;		szTemp = an 18-character buffer.
; Exit:		szTemp = the converted result.
			align	8
FloatToBCD2		proc	uses esi edi
			sub	esp,10
			; The fbstp instruction converts the top of the stack to a
			; packed BCD form in ten bytes, with two digits per byte.
			; The top byte has the sign, which we ignore.
			fbstp	[esp]
			; Now we need to unpack the BCD to ASCII.
			lea	esi,[esp+8]
			lea	edi,[szTemp]
			xor	ecx,ecx
			mov	[edi],ecx
			mov	[edi+4],ecx
			mov	[edi+8],ecx
			mov	[edi+12],ecx
			mov	[edi+16],cx
			mov	ecx,9
			.repeat
				mov	al,[esi]	; xxxx xxxx AAAA BBBB
				dec	esi
				rol	ax,12		; BBBB xxxx xxxx AAAA
				rol	ah,4		; xxxx BBBB xxxx AAAA
				and	ax,0f0fh	; 0000 BBBB 0000 AAAA
				add	ax,3030h	; 3B3A
				mov	[edi],ax
				add	edi,2
				dec	ecx
			.until zero?
			add	esp,10
			ret
FloatToBCD2		endp

;----------------------------------------------------------------------------------------------
; Convert a double precision number to a string.
; Entry:	fpin = 8-bytes double to convert
;		szDbl = character buffer
; Exit:		szDbl = converted value
; szDbl should be at least 19 bytes long.
			align	8
FloatToString		proc	uses ebx ecx edx esi edi fpin:qword,szDbl:ptr char
			local	iExp:dword
			local	stat:word
			local	mystat:word

; Special case zero.  fxtract fails for zero.
			mov	edi,[szDbl]
			.if (dword ptr [fpin] == 0) && (dword ptr [fpin][4] == 0)
				mov	byte ptr [edi],'0'
				mov	byte ptr [edi][1],0
				ret
			.endif
; Check for a negative number.
			.if (sdword ptr [fpin][4] < 0)
				and	byte ptr [fpin][7],07fh	; change to positive
				mov	byte ptr [edi],'-'	; store a minus sign
				inc	edi
			.endif
; Initialize the floating point unit and load our value onto the stack.
			fclex
			fstcw	[stat]
			mov	[mystat],027fh
			fldcw	[mystat]
			fld	[fpin]
			fld	st(0)				; (Dup. top stack)
; Compute the closest power of 10 below the number.  We can't get an
; exact value because of rounding.  We could get close by adding in
; log10(mantissa), but it still wouldn't be exact.  Since we'll have to
; check the result anyway, it's silly to waste cycles worrying about
; the mantissa.
;
; The exponent is basically log2(fpin).  Those of you who remember
; algebra realize that log2(fpin) x log10(2) = log10(fpin),
; which is what we want.
			fxtract			; ST=> mantissa, exponent, fpin
			fstp	st(0)		; drop the mantissa
			fldlg2			; push log10(2)
			fmulp	st(1),st	; ST = log10(fpin), fpin
			fistp	[iExp]		; ST = fpin
; An 8-byte double can carry almost 16 digits of precision.  Actually, it's
; 15.9 digits, so some numbers close to 1E17 will be wrong in the bottom
; digit.  If this is a concern, change the '16' to a '15'.
;
; A 10-byte double can carry almost 19 digits, but fbstp only stores the
; guaranteed 18.  If you're doing 10-byte doubles, change the '16' to '18'.
			.if ([iExp] < 16)
				fld	st(0)		; ST = fpin, fpin
				frndint			; ST = int(fpin), fpin
				fcomp	st(1)		; ST = fpin, status set
				fstsw	ax
				.if (ah & FP_EQUALTO)		; if EQUAL
; We have an integer!  Lucky day.  Go convert it into a temp buffer.
					call	FloatToBCD2
					mov	eax,17
					mov	ecx,[iExp]
					sub	eax,ecx
					inc	ecx
					lea	esi,[szTemp][eax]
; The off-by-one order of magnitude problem below can hit us here.  
; We just trim off the possible leading zero.
					.if (byte ptr [esi] == '0')
						inc	esi
						dec	ecx
					.endif
; Copy the rest of the converted BCD value to our buffer.
					rep	movsb
					jmp	ftsExit
				.endif
    			.endif
; Have fbstp round to 17 places.
			mov	eax,16		; experiment
			sub	eax,[iExp]	; adjust exponent to 17
			call	PowOf10
; Either we have exactly 17 digits, or we have exactly 16 digits.  We can
; detect that condition and adjust now.
			fcom	[ten16]
			; x0xxxx00 means top of stack > ten16
			; x0xxxx01 means top of stack < ten16
			; x1xxxx00 means top of stack = ten16
			fstsw	ax
			.if (ah & 1)
				fmul	[ten]
				dec	iExp
			.endif
			; Go convert to BCD.
			call	FloatToBCD2
			lea	esi,[szTemp+1]		; point to converted buffer
; If the exponent is between -15 and 16, we should express this as a number
; without scientific notation.
    			mov	ecx,iExp
			.if (SDWORD PTR ecx >= -15) && (SDWORD PTR ecx <= 16)
; If the exponent is less than zero, we insert '0.', then -ecx
; leading zeros, then 16 digits of mantissa.  If the exponent is
; positive, we copy ecx+1 digits, then a decimal point (maybe), then 
; the remaining 16-ecx digits.
				inc	ecx
				.if (SDWORD PTR ecx <= 0)
					mov	word ptr [edi], '.0'
					add	edi,2
					neg	ecx
					mov	al,'0'
					rep	stosb
					mov	ecx,16
				.else
					rep	movsb
					mov	byte ptr [edi],'.'
					inc	edi
					mov	ecx,16
					sub	ecx,[iExp]
				.endif
				rep	movsb
; Trim off trailing zeros.
				.while (byte ptr [edi-1] == '0')
					dec	edi
				.endw
; If we cleared out all the decimal digits, kill the decimal point, too.
				.if (byte ptr [edi-1] == '.')
					dec	edi
				.endif
				jmp	ftsExit
			.endif
; Now convert this to a standard, usable format.  If needed, a minus
; sign is already present in the outgoing buffer, and edi already points
; past it.
			movsb				; copy the first digit
			mov	byte ptr [edi],'.'	; plop in a decimal point
			inc	edi
			movsd				; copy four more digits
			movsw				; copy two more digits
; The printf %g specified trims off trailing zeros here.  I dislike
; this, so I've disabled it.  Comment out the if 0 and endif if you want this.
			if 0
				.while (byte ptr [edi][-1] == '0')
					dec	edi
				.endw
			endif
; Shove in the exponent.  If you support 10-byte reals, remember to
; allow 4 digits for the exponent.
			mov	byte ptr [edi],'e'	; start the exponent
			mov	eax,[iExp]
			.if (sdword ptr eax < 0)	; plop in the exponent sign
				mov	byte ptr [edi][1],'-'
				neg	eax
			.else
				mov	byte ptr [edi][1],'+'
			.endif
			mov	ecx,10
			xor	edx,edx
			div	ecx
			add	dl,'0'
			mov	[edi][4],dl	; shove in the ones exponent digit
			xor	edx,edx
			div	ecx
			add	dl,'0'
			mov	[edi][3],dl	; shove in the tens exponent digit
			xor	edx,edx
			div	ecx
			add	dl,'0'
			mov	[edi][2],dl	; shove in the hundreds exponent digit
			add	edi,5		; point to terminator
			; Clean up and go home.
ftsExit:		mov	byte ptr [edi],0
			fldcw	[stat]		; restore control word
			fwait
			ret
FloatToString		endp

;----------------------------------------------------------------------------------------------
; Set the cursor shape
			align	8
SetConCursor:		mov	CursorShape.dwSize,100
			cmp	InsertMode,1
			je	@F
			mov	CursorShape.dwSize,20
@@:			invoke	SetConsoleCursorInfo,hStdOut,addr CursorShape
			ret

;----------------------------------------------------------------------------------------------
; Set a pointer in
; eax must contains pointers array
			align	8
PutTabPointer		proc	Letter:dword,TabEntry:dword
			mov	ebx,Letter
			mov	ecx,TabEntry
			mov	[eax+ebx*4],ecx
			sub	ebx,20h			; Upper case
			mov	[eax+ebx*4],ecx
			ret
PutTabPointer		endp

;----------------------------------------------------------------------------------------------
; Fill a memory array
			align	8
FillMem			proc	uses eax ecx edi Mem:dword,Bytes:dword,Dat:dword
			mov	edi,Mem
			mov	ecx,Bytes
			cld
			mov	eax,Dat
			shr	ecx,2
			rep	stosd
			mov	ecx,Bytes
			and	ecx,3
			rep	stosb
			ret
FillMem			endp

;-----------------------------------------------------------------------------------------------
; Add an operator into wrap expressions buffer
			align	8
AddWrapOperator		proc	OperatorNumber:byte
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	bl,OperatorNumber
			mov	[eax],bl
			inc	CompExprPtr
			pop	ebx
			pop	eax
			ret
AddWrapOperator		endp

;-----------------------------------------------------------------------------------------------
; Add an integer into wrap expressions buffer
			align	8
AddWrapInteger		proc	IntegerNumber:dword
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	ebx,IntegerNumber
			mov	byte ptr [eax],WRAP_INT
			mov	[eax+1],ebx
			add	CompExprPtr,4+1
			pop	ebx
			pop	eax
			ret
AddWrapInteger		endp

;-----------------------------------------------------------------------------------------------
; Add a string reference into wrap expressions buffer
			align	8
AddWrapString		proc	StringAddress:dword
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	ebx,StringAddress
			mov	byte ptr [eax],WRAP_STRING
			mov	[eax+1],ebx
			add	CompExprPtr,4+1
			pop	ebx
			pop	eax
			ret
AddWrapString		endp

;-----------------------------------------------------------------------------------------------
; Add a floating point into wrap expressions buffer
			align	8
AddWrapFp		proc	FpNumber:qword
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	byte ptr [eax],WRAP_FP
			mov	ebx,dword ptr FpNumber
			mov	[eax+1],ebx
			mov	ebx,dword ptr FpNumber+4
			mov	[eax+4+1],ebx
			add	CompExprPtr,8+1
			pop	ebx
			pop	eax
			ret
AddWrapFp		endp

;-----------------------------------------------------------------------------------------------
; Add a function into wrap expressions buffer
			align	8
AddWrapFnc		proc	FncNumber:dword
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	ebx,FncNumber
			mov	byte ptr [eax],WRAP_FNC
			mov	[eax+1],ebx
			add	CompExprPtr,4+1
			pop	ebx
			pop	eax
			ret
AddWrapFnc		endp

;-----------------------------------------------------------------------------------------------
; Add a variable into wrap expressions buffer
			align	8
AddWrapVar		proc	VarOffset:dword
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	ebx,VarOffset
			mov	byte ptr [eax],WRAP_VAR
			mov	[eax+1],ebx
			add	CompExprPtr,4+1
			pop	ebx
			pop	eax
			ret
AddWrapVar		endp

;-----------------------------------------------------------------------------------------------
; Add a var address into wrap expressions buffer
			align	8
AddWrapVarAddress	proc	VarOffset:dword
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	ebx,VarOffset
			mov	byte ptr [eax],WRAP_VARADDRESS
			mov	[eax+1],ebx
			add	CompExprPtr,4+1
			pop	ebx
			pop	eax
			ret
AddWrapVarAddress	endp

;-----------------------------------------------------------------------------------------------
; Add an array into wrap expressions buffer
			align	8
AddWrapArray		proc
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	byte ptr [eax],WRAP_ARRAY
			inc	CompExprPtr
			pop	ebx
			pop	eax
			ret
AddWrapArray		endp

;-----------------------------------------------------------------------------------------------
; Add an array address into wrap expressions buffer
			align	8
AddWrapArrayAddress	proc
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	byte ptr [eax],WRAP_ARRAYADDRESS
			inc	CompExprPtr
			pop	ebx
			pop	eax
			ret
AddWrapArrayAddress	endp

;-----------------------------------------------------------------------------------------------
; Add an open bracket into wrap expressions buffer
			align	8
AddWrapOpenBracket	proc
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	byte ptr [eax],WRAP_OPENBRACKET
			inc	CompExprPtr
			pop	ebx
			pop	eax
			ret
AddWrapOpenBracket	endp

;-----------------------------------------------------------------------------------------------
; Add a string padding into wrap expressions buffer
			align	8
AddWrapStringPadding	proc
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	byte ptr [eax],WRAP_STRPADDING
			inc	CompExprPtr
			pop	ebx
			pop	eax
			ret
AddWrapStringPadding	endp

;-----------------------------------------------------------------------------------------------
; Add a close bracket into wrap expressions buffer
			align	8
AddWrapCloseBracket	proc
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	byte ptr [eax],WRAP_CLOSEBRACKET
			inc	CompExprPtr
			pop	ebx
			pop	eax
			ret
AddWrapCloseBracket	endp

;-----------------------------------------------------------------------------------------------
; Negate a number
			align	8
AddWrapMinusSign	proc
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	byte ptr [eax],WRAP_MINUSSIGN
			inc	CompExprPtr
			pop	ebx
			pop	eax
			ret
AddWrapMinusSign	endp

;-----------------------------------------------------------------------------------------------
; Add an expression suffix reference into wrap expressions buffer
			align	8
AddWrapExprEnd		proc
			push	eax
			push	ebx
			mov	eax,CompExprPtr
			mov	byte ptr [eax],WRAP_EXPREND
			inc	CompExprPtr
			pop	ebx
			pop	eax
			ret
AddWrapExprEnd		endp

;-----------------------------------------------------------------------------------------------
; Evaluate a precompiled expression
; In:
; edx:   address of expression block
; Out:
; ebx:   contains final term type (either integer or string address)
; st(0): contains a possible floating point
			align	8
EvalExprCOMP:		call	EXPR2COMP
			push	ebx
			cmp	byte ptr [edx],WRAP_GREATEREQU	; ">="
			jne	Op_NOTEQCOMP
			inc	edx
			call	DoRelOpCOMP
			jc	@F
			mov	bl,al
@@:			jmp	EndExprCOMP
Op_NOTEQCOMP:		cmp	byte ptr [edx],WRAP_NOTEQU	; "<>"
			jne	Op_GTCOMP
			inc	edx
			call	DoRelOpCOMP
			jz	@F
			mov	bl,al
@@:			jmp	EndExprCOMP
Op_GTCOMP:		cmp	byte ptr [edx],WRAP_GREATER	; ">"
			jne	Op_LESSEQCOMP
			inc	edx
			call	DoRelOpCOMP
			jz	@F
			jc	@F
			mov	bl,al
@@:			jmp	EndExprCOMP
Op_LESSEQCOMP:		cmp	byte ptr [edx],WRAP_LOWEREQU	; "<="
			jne	Op_EQCOMP
			inc	edx
			call	DoRelOpCOMP
			mov	bl,al
			jz	@F
			jc	@F
			mov	bl,bh
@@:			jmp	EndExprCOMP
Op_EQCOMP:		cmp	byte ptr [edx],WRAP_EQU		; "="
			jne	Op_LESSCOMP
			inc	edx
			call	DoRelOpCOMP
			jnz	@F
			mov	bl,al
@@:			jmp	EndExprCOMP
Op_LESSCOMP:		cmp	byte ptr [edx],WRAP_LOWER	; "<"
			jne	Op_UnknownCOMP
			inc	edx
			call	DoRelOpCOMP
			jnc	@F
			mov	bl,al
@@:			jmp	EndExprCOMP
Op_UnknownCOMP:		pop	ebx
EndExprCOMP:		mov	al,[edx]
			cmp	al,WRAP_EXPREND			; Pass end flag
			jne	GetExprEndFlag
			inc	edx
GetExprEndFlag:		call	PopOperStack
			mov	ExprFinalResult,eax
			ret
			align	8
DoRelOpCOMP:		mov	al,cl
			pop	ebx
			pop	ecx
			push	ebx
			push	ecx
			mov	cl,al
			call	EXPR2COMP
			xchg	edx,ebx
			pop	eax
			push	ebx
			call	LoadOperTerms
			test	bl,bl
			jz	NormCompareCOMP
			invoke	PushOperStack,VAR_INTEGER
			call	CompareSignFlt
			pop	edx
			mov	ebx,0
			mov	al,1
			ret
			align	8
NormCompareCOMP:	mov	ebx,eax
			invoke	PushOperStack,VAR_INTEGER
			call	CompareSign
			pop	edx
			mov	ebx,0
			mov	al,1
			ret

;-----------------------------------------------------------------------------------------------
			align	8
EXPR2COMP:		cmp	byte ptr [edx],WRAP_MINUS	; "-"
			jne	X21COMP
			inc	edx
			xor	ebx,ebx
			invoke	PushOperStack,VAR_INTEGER
			jmp	X27COMP
			align	8
X21COMP:		call	EXPR3COMP
X23COMP:		cmp	byte ptr [edx],WRAP_PLUS	; "+"
			jne	X26COMP
			inc	edx
			push	ebx
			call	EXPR3COMP
X24COMP:		xchg	edx,ebx
			pop	eax
			push	ebx
			call	LoadOperTerms
			test	bl,bl
			jz	NormAddCOMP
			fld	FltTerm1
			fadd	FltTerm2
			invoke	PushOperStack,VAR_FLOAT
			pop	edx
			jmp	X23COMP
			align	8
NormAddCOMP:		invoke	PushOperStack,VAR_INTEGER
			mov	ebx,eax
			add	ebx,edx
			pop	edx
			jno	X23COMP
X25COMP:		mov	edi,offset Err2
			jmp	DispError
R4COMP:			ret			
			align	8
X26COMP:		cmp	byte ptr [edx],WRAP_MINUS	; "-"
			jne	R4COMP
			inc	edx
X27COMP:		push	ebx
			call	EXPR3COMP
			call	PeekOperStack
			cmp	eax,VAR_FLOAT
			je	ChangeFltSignCOMP
			call	ChangeSign
			jmp	X24COMP
			align	8
ChangeFltSignCOMP:	call	ChangeSignFlt
			jmp	X24COMP

;-----------------------------------------------------------------------------------------------
			align	8
EXPR3COMP:		call	Expr_GetTermCOMP
Op_MulCOMP:		cmp	byte ptr [edx],WRAP_MUL		; "*"
			jne	Op_DivCOMP
			inc	edx
			push	ebx
			call	Expr_GetTermCOMP
			xchg	edx,ebx
			pop	eax
			push	ebx
			call	LoadOperTerms
			test	bl,bl
			jz	NormMulCOMP
			fld	FltTerm1
			fmul	FltTerm2
			invoke	PushOperStack,VAR_FLOAT
PassOp4COMP:		jmp	X35COMP
			align	8
NormMulCOMP:		invoke	PushOperStack,VAR_INTEGER
			imul	eax,edx
			jo	X25COMP
			mov	ebx,eax
			jmp	X35COMP

;-----------------------------------------------------------------------------------------------
			align	8
Op_DivCOMP:		cmp	byte ptr [edx],WRAP_DIV		; "/"
			jne	Op_DivFltCOMP
			inc	edx
			push	ebx
			call	Expr_GetTermCOMP
			xchg	edx,ebx
			pop	eax
			push	ebx
			call	LoadOperTermsFlt
			fld	FltTerm2
			ftst
			fstsw	ax
			fstp	FltTerm2
			and	ax,100010100000000b
			cmp	ax,100000000000000b
			je	ExprDivZeroCOMP
PassOpDivCOMP:		fld	FltTerm1
			fdiv	FltTerm2
PassOp5COMP:		invoke	PushOperStack,VAR_FLOAT
			jmp	X35COMP
			align	8
NormDivCOMP:		mov	ebx,eax
			test	edx,edx
			jnz	@F
ExprDivZeroCOMP:	mov	edi,offset Err6			; Div/0
			jmp	DispError
			align	8
@@:			invoke	PushOperStack,VAR_INTEGER	
			push	ecx
			call	Divide
			mov	ebx,ecx
			pop	ecx
X35COMP:		pop	edx
			jmp	Op_MulCOMP

;-----------------------------------------------------------------------------------------------
			align	8
Op_DivFltCOMP:		cmp	byte ptr [edx],WRAP_INTDIV	; "\"
			jne	R4COMP
			inc	edx
			push	ebx
			call	Expr_GetTermCOMP
			xchg	edx,ebx
			pop	eax
			push	ebx
			call	LoadOperTerms
			cmp	ExecMode,EXECMODE_PRECOMP
			je	NormDivCOMP
			test	bl,bl
			jz	NormDivCOMP
			fld	FltTerm1
			frndint
			fistp	FltToInt
			mov	eax,FltToInt
			fld	FltTerm2
			frndint
			fistp	FltToInt
			mov	edx,FltToInt
			jmp	NormDivCOMP

;-----------------------------------------------------------------------------------------------
			align	8
Expr_GetTermCOMP:	xor	cl,cl
			mov	al,[edx]
			cmp	al,WRAP_MINUSSIGN
			jne	ToNegate
			xor	cl,1
			inc	edx
			mov	al,[edx]
ToNegate:		inc	edx
			push	ecx
			
; Functions
			cmp	al,WRAP_FNC
			jne	GetFunction
			mov	ebx,[edx]		; Function number
			add	edx,4
			jmp	[RunFncTbl+ebx*4]

; Variables
			align	8
GetFunction:		cmp	al,WRAP_VAR		; Variables
			jne	GetVariable
			mov	ebx,[edx]		; Var offset
	;		shl	ebx,4
			add	ebx,AZVar
			add	edx,4
LoadValueFromAddress:	mov	eax,[ebx+8]
			cmp	eax,VAR_FLOAT
			jne	FloatFromVar
			fld	qword ptr [ebx]		; FP datas
			invoke	PushOperStack,VAR_FLOAT
			pop	ecx
			test	cl,cl
			jz	FinalizeFPFromVar
			fchs
FinalizeFPFromVar:	ret
			align	8
FloatFromVar:		cmp	eax,VAR_STRING
			jne	StringFromVar
			mov	ebx,[ebx]
			invoke	PushOperStack,VAR_STRING
			pop	ecx
			test	cl,cl
			jnz	E26
			ret
			align	8
StringFromVar:		mov	ebx,[ebx]
			invoke	PushOperStack,VAR_INTEGER
NegateInteger:		pop	ecx
			test	cl,cl
			jz	FinalizeIntFromVar
			neg	ebx
FinalizeIntFromVar:	ret

; Array offset
			align	8
GetVariable:		cmp	al,WRAP_ARRAY
			jne	GetVarAddress
			call	GetArrayArgs		; We need to calculate array offset here
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			test	ebx,ebx
			js	E40
			cmp	ebx,ARRAY_NBR
			jnc	E3
			shl	ebx,4
			add	ebx,ArrayVar
			jmp	LoadValueFromAddress
			align	8
E3:			mov	edi,offset Err3		; No, Error : <Subscript out of array range>
			jmp	DispError

; Variable address
			align	8
GetVarAddress:		cmp	al,WRAP_VARADDRESS
			jne	GetArrayAddress
			mov	ebx,[edx]		; ebx has already been multiplied because variables
			add	ebx,AZVar		; are fixed in memory
			add	edx,4
			invoke	PushOperStack,VAR_INTEGER
			pop	eax
			test	al,al
			jnz	E26			; Negative address ? too weird
			ret

; Array address
			align	8
GetArrayAddress:	cmp	al,WRAP_ARRAYADDRESS
			jne	GetFP
			call	GetArrayArgs
			cmp	ExprFinalResult,VAR_INTEGER
			jne	E27
			test	ebx,ebx
			js	E40
			cmp	ebx,ARRAY_NBR
			jnc	E3
			shl	ebx,4
			add	ebx,ArrayVar
			invoke	PushOperStack,VAR_INTEGER
			pop	eax
			test	al,al
			jnz	E26			; Negative address ? too weird
			ret

; FP datas
			align	8
GetFP:			cmp	al,WRAP_FP
			jne	GetStringAddr
			fld	qword ptr [edx]
			add	edx,8
			invoke	PushOperStack,VAR_FLOAT
			pop	ecx
			test	cl,cl
			jz	FinalizeFP
			fchs
FinalizeFP:		ret

; FP datas
			align	8
GetStringAddr:		cmp	al,WRAP_STRING
			jne	GetInt
			mov	ebx,[edx]
			add	edx,4
			invoke	PushOperStack,VAR_STRING
			pop	ecx
			test	cl,cl
			jnz	E26			; Can't negate a string
FinalizeStr:		ret

; Int datas
			align	8
GetInt:			cmp	al,WRAP_INT
			jne	GetFNCArgsFromExpr
			mov	ebx,[edx]
			add	edx,4
			invoke	PushOperStack,VAR_INTEGER
			pop	ecx
			test	cl,cl
			jz	FinalizeInt
			neg	ebx
FinalizeInt:		ret

; Array cells
			align	8
GetArrayArgs:		mov	al,[edx]
			cmp	al,WRAP_OPENBRACKET
			jne	E29
			inc	edx
			call	EvalExprCOMP
			invoke	PushOperStack,ExprFinalResult
			mov	al,[edx]
			cmp	al,WRAP_CLOSEBRACKET
			jne	E30
			inc	edx
			ret

; Sub expressions
			align	8
GetFNCLastArgs:		mov	ExprFinalResult,VAR_NOFNCARG
			jmp	ContFncArg
			align	8
GetFNCArgs:		mov	ExprFinalResult,VAR_NOFNCARG
			mov	al,[edx]		; load char if coming from a function
			cmp	al,WRAP_OPENBRACKET	; (
			jne	GetNextFncExpr
			inc	edx			; Avoid some recursive problems
ContFncArg:		mov	al,[edx]		; Empty function argument ?
			cmp	al,WRAP_CLOSEBRACKET	; )
			je	EmptyFncExpr
			call	EvalExprCOMP
			invoke	PushOperStack,ExprFinalResult
			jmp	GetNextFncExpr
EmptyFncExpr:		mov	ExprFinalResult,VAR_NOFNCARG
GetNextFncExpr:		mov	al,[edx]
			cmp	al,WRAP_CLOSEBRACKET	; )
			jne	FinalizeBracketCOMP
			inc	edx
FinalizeBracketCOMP:	ret

GetFNCArgsFromExpr:	cmp	al,WRAP_OPENBRACKET	; (
			jne	GetNextExpr
			call	EvalExprCOMP
			invoke	PushOperStack,ExprFinalResult
GetNextExpr:		mov	al,[edx]
			cmp	al,WRAP_CLOSEBRACKET	; )
			jne	FinalizeFncCOMP
			inc	edx
FinalizeFncCOMP:	pop	eax
			test	al,al
			jz	NoNegFncCOMP
			call	PeekOperStack
			cmp	eax,VAR_FLOAT
			jne	NegIntFltFncCOMP
			fchs
			ret
			align	8
NegIntFltFncCOMP:	cmp	eax,WRAP_STRING
			je	E26
			neg	ebx
NoNegFncCOMP:		ret
			align	8
E40:			mov	edi,offset Err40	; Error: <Invalid cell reference>
			jmp	DispError

;-----------------------------------------------------------------------------------------------
end start
