;Mini-Basic, Copyleft Sylvain BIZOIRRE 01-2001
;Please, report any questions, bugs, enhancements at : sbfg@wanadoo.fr

;Mini-Basic was made with MASM32, a free assembler environment you can freely download at :
;http://www.movsd.com/masm.htm

      .586                              ;Only 1 pentium instruction is used in RND command.
                                        ;All other instructions are 486 compatible
      .model flat, stdcall
      option casemap :none   ; case sensitive
; #########################################################################
      include \masm32\include\windows.inc
      include \masm32\include\kernel32.inc
      include \masm32\include\user32.inc
      include \masm32\include\winmm.inc

      includelib \masm32\lib\kernel32.lib
      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\winmm.lib
; #########################################################################

.data

STD_INPUT_HANDLE        EQU -10         ;Standard console input handle
STD_OUTPUT_HANDLE       EQU -11         ;Standard console output handle

;-----------------------------------------------------------------------------------------------
;Following variables can be modified to change Mini-Basic features and appearence
MEMSIZE                 EQU 0FFFFh      ;Size of memory allocated for Basic progs (here 64 Ko.)
ARRAY_NBR               EQU 2000        ;Number of cells for @(array)
PromptChar              DB  ">"         ;Char. used for prompt
EditColors              DD  0F0h        ;Editor colors (Grey background, Black foreground)
EditSizeX               DW  100         ;Editor screen sizes 
EditSizeY               DW  25
PromptColor             DB  0Ch         ;Prompt color (here red)

;----------------------------------------------------------------------------------------------- 

dtemp1                  DD 0            ;Temporary 32 bits Variables
dtemp2                  DD 0
dtemp3                  DD 0
dtemp4                  DD 0
dtemp5                  DD 0 
wtemp1                  DW 0            ;Temporary 16 bits Variables
wtemp2                  DW 0
btemp1                  DB 0            ;Temporary 8 bits variable 
btemp2                  DB 0  
BasicTitle              DB "Windows Console Tiny Basic V.1", 0Dh

;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

ColorsCurrent            DD 0           ;Current colors used

EventBuffer              DB 16 DUP(0)   ;Informations area for keyboard inputs 

FileHandle               DD 0           ;Given by CreateFile API (DLOAD, DSAVE)
FileTitle                DB 12 DUP(0)   ;File title area (8 char. + '.MBI' + 0)   
FileExt                  DD "IBM."      ;File extension automatically added (reverse order!)
ForStackPtr             DD 0            ;FOR..NEXT Stack pointer
ForAdrVar               DD 0            ;Adress of FOR loop variable 
ForLimit                DD 0            ;Higher value of FOR variable
ForIncrement            DD 0            ;STEP increment or 1
ForLine                 DD 0            ;Line to go back with NEXT  
ForPtrTxt               DD 0            ;Adress to go back with NEXT    

hStdIn                   DD 0           ;Standard input and output handles given by Windows
hStdOut                  DD 0

InsertMode               DB 0           ;Insert key flag in line editor (1 = Insert, 0 = Normal)
LineLen                  DD 0           ;Input Line Length 
LineMaxLen               DD 0           ;Maximum input line length allowed 
LineNumber               DD 0           ;Current used line number 
LinePtr                  DD 0           ;Input line ptr
ConsoleCharNbr           DD 0           ;Number of char. in current console screen  

Disp1Char                DB " ",0       ;Only 1 char to display
ReadyTxt                 DB "Ready.", 0Dh
ClearInput               db "          ",0 ;Fill input area with spaces

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

Sstack                   DD 0           ;Here is saved the stack pointer
WinTitle                 DB "Mini-Basic V.1",0

TxtEnd                   DD 0           ;End of Basic Text pointer

;Virtual memory Pointers
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
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)
AZVar                    DD 0           ;Variables A to Z area (26 * Dword)   
ArrayVar                 DD 0           ;Array variables area @(EXP) (2048 * Dword)
;-----------------------------------------------------------------------------------------------
;Mini-Basic code starts here.
.code
start:
;Allocate necessary virtual memory for buffers, Basic text and @(array) 
; !!! ALLOCATION ORDER MUST NOT BE CHANGED !!!
    mov     eax, 1100 + MEMSIZE + ARRAY_NBR*4
    invoke  VirtualAlloc, 0, eax, MEM_COMMIT + MEM_RESERVE, PAGE_READWRITE
    cmp     eax, 0
    je      Error                       ;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
    mov     AZVar, eax
    add     eax, 104                    ;Variables A to Z and @(array) areas
    mov     ArrayVar, eax
 
    ;invoke  FreeConsole                 ;Free original Window console

    call    AllocCons                   ;and allocate a new one    
    call    WinEdit                     ;Set window size and colors to standard Mini basic editor

    mov     Sstack, esp                 ;Save current stack adress
    cld                                 ;Direction flag = 0, for strings transfer

    mov     eax, TxtStart               ;NEW command equivalent
    mov     TxtEnd, eax    
    mov     LineMaxLen, 255             ;Basic text line maximum length
    xor     al, al                      ;Nothing in AL before printing Title
    mov     edx, OFFSET BasicTitle      ;Display Basic title
    call    DispStr                    
Ready:      ;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 Affiche_CrLf before 'Ready'
    call    Affiche_CrLf
@@: mov     edx, OFFSET ReadyTxt        ;Display "Ready"
    call    DispStr  
Prompt:     ;Print prompt and wait for user input line. Basic variables are not cleared 
    mov     esp, Sstack                 ;Stack pointer retrieve original value
    push    ColorsCurrent
    xor     eax,eax
    mov     al, 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     
ED: mov     eax, 0                      ;Noting allowed in output (no auto-CR & scroll)
    invoke  SetConsoleMode, hStdOut, eax
    call    TestApiError
    mov     dtemp1, 18                  ;Cursor hight
    mov     dtemp2, 1                   ;and visible
    invoke  SetConsoleCursorInfo, hStdOut, ADDR dtemp1
    call    TestApiError
    invoke  FlushConsoleInputBuffer, hStdIn  ;Clear keyboard input buffer  

    call    LineInput                        ;Wait for user to type a line 

    ;mov     eax, ENABLE_WRAP_AT_EOL_OUTPUT   ;CR & scroll allowed in output
    ;invoke  SetConsoleMode, hStdOut, eax
    ;call    TestApiError
    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
    or      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 : <Line number too large>
    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 adress = 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    FindNextLign                ;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    MouvDown                    ;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
;-----------------------------------------------------------------------------------------------
;'InitP' initialize FOR..NEXT and GOSUB stacks pointers
InitP:    
    xor     eax,eax                    
    mov     LineNumber,eax              ;Init some pointers
    mov     GosLineNum, eax             ;To check RETURN without GOSUB                
    mov     eax, GosubStack             ;GosStackPtr on top of GOSUB stack 
    mov     GosStackPtr, eax            ;GosStackPtr goes up
    mov     ForStackPtr, eax            ;ForStackPtr goes down
    ret
;-----------------------------------------------------------------------------------------------
;Test if a variable A to Z or array '@(EXPR)' can be found.
; If found, c=0, ebx=variable adress, else c=1 [registers used : ax, ebx]
TestVar:
    xor     ebx,ebx
    mov     ah,'@'                      ;Is it a variable ?
    call    SkipSpaces       
    jc      TV2                         ;No, return to caller
    jnz     TV1                         ;Yes, it's a letter variable, treat it
    call    TestParenth                 ;     it's an array @ followed by '(EXP)' 
    cmp     ebx, ARRAY_NBR              ;     EXP (ebx) < total cells in @ array ?
    jc      @F                          ;     Yes, OK
    mov     edi, OFFSET Err3            ;     No, Error : <Subscript out of array range>
    jmp     DispError
@@: sal     ebx, 2                      ;ebx = ebx * 4 (4 bytes per array value)
    add     ebx, ArrayVar               ;ebx= Adresse of '@(EXP)' value & return with c=0
TV2:ret                                 

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, 2                      ;     ebx=ebx*4 (4 bytes per variable value)
    add     ebx, AZVar                  ;     ebx = adress (AZVar-4)+(ebx*4)
    ret                                 ;     ret with c=0
;-----------------------------------------------------------------------------------------------
; Test an ASCII integer 32 bits number from [edx]. ebx = binary result or 0 if not.
; [registers used : eax, ecx]  
TstNum:
    mov     ebx,0                        ;bx = binary result
    mov     ch,bh                        ;ch=0. Char. counter
    sub     ah,ah                        ;cancel SkipSpaces comparison
    call    SkipSpaces                   ;al = first char. found
    ;conversion loop
@@: cmp     al,'0'                       ;char al must be between '0' & '9'. If not, return
    jc      R1                           
    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
    jns     @B                           ;Next char. or error if sign Flag = 1
E2: mov     edi, OFFSET Err2             ;Error : <numeric value too high>
    jmp     DispError
R1: ret
;-----------------------------------------------------------------------------------------------
; Keywords table
; This table can contain an undefined number of keywords. Each of them is a string with
; bit 7 of last char. set to 1 (+127). The jump adress of the corresponding routine just
; follow the keyword.
; The keyword table ends with a byte of value 128, and an adress to jump if
; the keyword have not been found. 
.data
TAB1    EQU     $                       ;Direct keywords, forbidden in a program
 DB      "LIS",0D4H                     ;'LIST'
 DD      OFFSET LIST                    ;Adresse of LIST routine
 DB      "EDI",0D4H                     ;'EDIT'
 DD      OFFSET EDIT 
 DB      "RU",0CEH                      ;'RUN'
 DD      OFFSET RUN 
 DB      "NE",0D7H                      ;'NEW'
 DD      OFFSET NEW 
 DB      "LOA",0C4H                     ;'LOAD'
 DD      OFFSET DLOAD 
 DB      "SAV",0C5H                     ;'SAVE'
 DD      OFFSET DSAVE 
TAB2    EQU     $                 ;Keywords allowed in a program
 DB      "NEX",0D4H                     ;'NEXT
 DD      OFFSET NEXT 
 DB      "BEE",0D0H                      ;'BEEP'
 DD      OFFSET BIP  
 DB      "POK",0C5H                     ;'POKE'
 DD      OFFSET POKE 
 DB      "WAI",0D4H                     ;'WAIT'
 DD      OFFSET WWAIT 
 DB      "I",0C6H                       ;'IF'
 DD      OFFSET IFF 
 DB      "GOT",0CFH                     ;'GOTO'
 DD      OFFSET GO_TO 
 DB      "GOSU",0C2H                    ;'GOSUB'
 DD      OFFSET GOSUB 
 DB      "RETUR",0CEH                   ;'RETURN'
 DD      OFFSET RETURN 
 DB      0A7H                           ;'''  (REM)
 DD      OFFSET REM 
 DB      "FO",0D2H                      ;'FOR'
 DD      OFFSET FFOR 
 DB      "INPU",0D4H                    ;'INPUT'
 DD      OFFSET INPUT 
 DB      "PRIN",0D4H                    ;'PRINT'
 DD      OFFSET PRINT 
 DB      "WINDO",0D7H                   ;'WINDOW'
 DD      OFFSET WINDOW 
 DB      "STO",0D0H                     ;'STOP'
 DD      OFFSET STOP 
 DB      "COLO",0D2H                    ;'COLOR'
 DD      OFFSET COLOR 
 DB      "LOCAT",0C5H                   ;'LOCATE'
 DD      OFFSET LOCATE 
 DB      "CL",0D3H                      ;'CLS'
 DD      OFFSET CLS 
 DB      "GE",0D4H                      ;'GET'
 DD      OFFSET GET 
 DB      "DEBU",0C7H                    ;'DEBUG' (Does nothing, only used for debugging)
 DD      OFFSET DEBUG 
 DB      128                      ;End of command keywords table
 DD      OFFSET CrOrLet                 ;Jump to 'CrOrLet' if keyword not found 
                                        ;(empty line or variable affectation)

TAB4    EQU     $                 ;Functions Table.
 DB      "RN",0C4H                      ;'RND'
 DD      OFFSET RND 
 DB      "PEE",0CBH                     ;'PEEK'
 DD      OFFSET PEEK 
 DB      "AB",0D3H                      ;'ABS'
 DD      OFFSET ABS 
 DB      "SIZ",0C5H                     ;'SIZE'
 DD      OFFSET SIZZE 
 DB      128                      ;End of functions table
 DD      OFFSET X40                     ;If keyword not found, test a variable
TableTO  EQU     $                ;Table reserved for keyword 'TO'
 DB      "T",0CFH                       ;'TO' of 'FOR...NEXT' loop
 DD      OFFSET FR1 
 DB      128             
 DD      OFFSET ErrorTO                 ; Error, missing 'TO' 
TableSTEP EQU     $               ;Table reserved for keyword 'STEP'
 DB      "STE",0D0H                     ;'STEP' of 'FOR...NEXT' loop 
 DD      OFFSET FR2 
 DB      128             
 DD      OFFSET FR3
TableEDIT EQU     $               ;Table reserved for keyword 'EDIT' as option of WINDOW
 DB      "EDI",0D4H                     
 DD      OFFSET WD2 
 DB      128             
 DD      OFFSET WD3 
TAB8     EQU     $                ;Relational operators table.
 DB      ">",0BDH                       ;'>='
 DD      OFFSET X11 
 DB      "<",0BEH                       ;'<>'
 DD      OFFSET X12 
 DB      0BEH                           ;'>'
 DD      OFFSET X13 
 DB      0BDH                           ;'='
 DD      OFFSET X15 
 DB      "<",0BDH                       ;'<='
 DD      OFFSET X14 
 DB      0BCH                           ;'<'
 DD      OFFSET X16 
 DB      128                      ;End of operators table.
 DD      OFFSET X17 
.code
;                                 ;End of table  
;----------------------------------------------------------------------------------------------- 
;'DEBUG' does nothing. Only used to trace, with ASM debugger, next command in a Mini-Basic program 
DEBUG:
    jmp     NextCmd
;----------------------------------------------------------------------------------------------- 
; Jump here if keyword 'TO' isn't found in a FOR..TO loop
ErrorTO:
    mov     edi, OFFSET Err5
    jmp     DispError
;----------------------------------------------------------------------------------------------- 
;'Execute' compare a string pointed by ebx with a keywords table pointed by edx
; If string is found, execution jump to OFFSET below the keyword string in the table   
; If string not found, execution jump to OFFSET below last value 128 of the table
;'DirectCmd' looks for Direct commands in table TAB1. 
;
DirectCmd:
    mov     ebx,OFFSET TAB1-1           ;ebx points to keywords table (-1)
Execute:
    mov     ah,0                        ;No char. for SkipSpaces
    call    SkipSpaces
    push    edx                         ;Save text pointer
EX1:mov     al,[edx]                    ;al = text letter 
    inc     edx                         ;Text pointer +1
    inc     ebx                         ;Keywords table pointer +1
    mov     ah,[ebx]                    ;ah = keyword letter
    AND     ah,127                      ;Cancel bit 7
    cmp     al,ah                       ;and compare...
    jz      EX2                         ;yes, identicals
@@: cmp     BYTE PTR [ebx],128          ;No, loop to find last table keyword letter (ASCII code + 128)
    jnc     @F                          
    inc     ebx            
    jmp     @B        
@@: add     ebx,5                       ;Last letter found, skip keyword jump adress
    dec     edx                         
    cmp     BYTE PTR[ebx],128           ;End of table ?
    pop     edx                         ;Restore text pointer
    jz      @F                          ;Yes, try an expression or error
    dec     ebx                         ;No, ebx = next keyword
    jmp    Execute                      ;Test next keyword

EX2:cmp     BYTE PTR [ebx],128          ;if letter ASCII code < 128, test next letter
    jc      EX1
    pop     eax                         ;adress, stack is cleared,
@@: inc     ebx                         ;else, keyword found. ebx points on routine execution 
    jmp     DWORD PTR [ebx]             ;and jump to routine adress
;-----------------------------------------------------------------------------------------------
; NEW cancels text program in memory. In fact, only modify Txtend pointer
NEW:    
    call  TestCR                        ;Must be NEW(CR)                   
    mov   eax, TxtStart  
    mov   TxtEnd, eax                   ;TxtEnd = start adresse of basic text area    
    jmp   Ready                         ;Reset pointers before prompt
;-----------------------------------------------------------------------------------------------
; RUN(CR) finds the first stored line, stores it's adress and starts to execute it.
; Note that only TAB2 commands are legal for stored programs
;
; There are three more entries in 'RUN' :
;       'NextLine' finds the next line, stores it's adress and executes it
;       'ExecLine' Stores the adress of this line ans executes it
;       'ContLine' Continues the execution on same line
RUN:
    call    TestCR                      ;must be RUN(CR)
    call    InitP                       ;Init. FOR..NEXT and GOSUB stacks pointers
    mov     edx, TxtStart               ;edx = first Basic text program adress
NextLine:
    xor     ebx,ebx                     ;Line number = 0
    call    LineNumCompare              ;End of Basic text ?
    jnc     ExecLine                    ;No, execute line
    jmp     Ready                         
;----------------------------------------
; 'STOP' is a simple jump to InputLoop, keeping program variables their values
; FOR..NEXT and GOSUB stacks are not modified
STOP:
    mov     LineNumber, 0               
    jmp     Prompt                      
;----------------------------------------
ExecLine:
    call    TestInpBuf                  ;Escape key pressed ?
    cmp     bl,27                        
    je      Prompt                      ;Yes, variables are not canceled and can be printed
    mov     ebx, [edx]                  ;No, Line number in location LineNumber
    mov     LineNumber, ebx
    add     edx,4                       ;edx skip binary line number & points on 1st. line char
ContLine:
    mov     ebx, TAB2-1                 ;Executable codes table
    jmp     Execute                     ;and execute
;-----------------------------------------------------------------------------------------------
;'GOTO(EXPR)' evaluates the expression, finds the target line and jumps to Execline to do it
GO_TO:
    call    EvalExpr                    ;Evaluate expression
    push    edx                         ;Save line number in case of error.
    call    FindLine                    ;Look for line expression
    jz      @F                          ;Found line number, execute it
    mov     edi, OFFSET Err10           ;else, error <Line number not found>
    jmp     DispError
@@: 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 Mini basic editor
; (See problems in DOC file)
WINDOW:
    call    TstNum                      ;Check 1st. parameter
    or      ch,ch
    jz      WD1                         ;Number not detected, may be (EDIT)
    mov     si, bx                      ;Detected, si = window width
    call    FindComma                   ;followed by a ','
    jnz      E17                        ;If not, error <',' expected>    
    call    TstNum                      ;Check second parameter
    or      ch,ch
    jz      E18                         
    mov     di, bx                      ;di = window height
    push    edx
    call    RedimWindow                 ;Resize window
    pop     edx
    jmp     NextCmd                     ;and continue execution
WD1:mov     ebx, TableEDIT-1            ;'EDIT' keyword after 'WINDOW' ?
    jmp     Execute                     ;If Yes, execute WD2 code, else jump to WD3
WD2:push    edx
    call    WinEdit
    pop     edx
    jmp     NextCmd
WD3:jmp     E18                         ;Not a parameter and not (EDIT), error <Bad parameter>
;-----------------------------------------------------------------------------------------------
;Allocates standard Input and Output handles for a new console screen 
AllocCons:
    ;invoke  AllocConsole
    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 ans colors
WinEdit:
    mov     eax, EditColors             ;Set editor colors
    mov     ColorsCurrent, eax          ;
    mov     si, EditSizeX               ;and sizes       
    mov     di, EditSizeY                      
    call    RedimWindow                 ;Redim console window & buffer
WE1:mov     eax, ColorsCurrent          ;fill console screen with current colors attributes
    xor     ecx, ecx                    ;Stating at cursor position 0,0 
    mov     edx, ConsoleCharNbr         ;for all console chars.
    invoke  FillConsoleOutputAttribute, hStdOut, eax, edx, ecx, ADDR dtemp1
    call    TestApiError
    mov     eax, 32                     ;Char. to fill screen with (space)
    mov     ebx, ConsoleCharNbr         ;Nbr. of char. in console screen
    invoke  FillConsoleOutputCharacter, hStdOut, eax, ebx, 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  SetConsoleTitleA, ADDR WinTitle
    call    TestApiError
    ret
;-----------------------------------------------------------------------------------------------
; LIST has two forms : LIST(CR) lists all saved Basic lines
;                    : LIST Number(CR) start list at line number or next if not found
; You can pause listing with 'Space' key and stop listins with 'ESC' key
LIST:
    call    TstNum                      ;Test if there is a line number
    call    TestCR                      ;If not, number is 0
    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
@@: 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,50                      ;50 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
EDIT:
    call    TstNum                      ;Is there a line number ? If not, edit the first line nbr.
    call    TestCR                      ;Test CR or Error 
    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
@@: mov     ch,0                        ;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
    mov     dx, Csbi_MaximumWindowSizeX
    push    ebp                         ;ebp = Number of line chars.
@@: dec     bx
    sub     bp, dx
    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
    invoke  ReadConsoleOutputCharacterA, hStdOut, edi, ebp, DWORD PTR Csbi_CursorPositionX, ADDR dtemp1
    call    TestApiError
    mov     LinePtr, 1
    pop     ebp
    inc     ebp
    mov     LineLen, ebp
    jmp     ED                          ;then jump to the editor
;-----------------------------------------------------------------------------------------------
;'Ucase' converts all typed line char. in upper case, except "strings" and REM, from edx to ecx
Ucase:        
    mov     btemp1,0                    ;Reset quote flag
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
@@: cmp     btemp1,1                    ;String is being read ?
    je      @F                          ;Yes, ignore this char.
    cmp     al, "'"                     ;REM ?
    je      UC2                         ;Yes, nothing more to modify in the line. Return to caller 
    cmp     al, "a"                     ;If char = 'a' to 'z'
    jb      @F
    cmp     al, "z"
    ja      @F
    sub     al, 32                      ;convert it in upper case
    mov     [edx],al
@@: inc     edx
    jmp     UC1
UC2:ret
;-----------------------------------------------------------------------------------------------
; PRINT command is 'PRINT ....;' or 'PRINT ....(CR)'
; where '....' is a list of expressions, formats, backarrows and strings.
; theses 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. If no format specified, 6 positions will be used.
;
; A string is quoted in a pair of double quotes.
;
; A back-arrow means generate ac (CR) without (LF).
;
; A (CRLF) is generated after the entire lise has been print or if the list is a null list.
; However, if the list ended with a comma, no (CR) is generated
PRINT:
    mov     cl,6
    mov     ah,':'                      ;Is a print followed by ':' ?
    call    SkipSpaces  
    jnz     @F                          ;No, test other possibilities
    call    Affiche_CrLf                        ;Yes, print CRLF and execute the rest of the line
    jmp     ContLine  
@@: mov     ah,0dh                      ;End of line ?        
    call    SkipSpaces
    jnz     PR0                         ;No, test other possibilities
    call    Affiche_CrLf                        ;Yes, print CRLF and go to next line
    jmp     NextLine  
PR0:mov     ah,'#'                      ;Format sign ?
    call    SkipSpaces
    jnz     @F                          ;No, test other possibilities
    call    EvalExpr                    ;Yes, evaluate expression
    mov     cl,bl                       ;and save it in cl
    jmp     PR3                         ;Look for more to print
@@: call    QuotedString                ;or is it a string ?
    jmp     PR8                         ;if not, must be an expression
PR3:mov     ah, ";"                     ;Is it a ';'
    call    SkipSpaces
    jnz     @F                          ;No, must be end of PRINT command  
    call    CmdEnd                      ;Yes, run next command if find ':' or CR 
    jmp     PR0                         ;or continue print list
@@: push    eax
    call    Affiche_CrLf                        ;List ends
    pop     eax
    jmp     NextCmd                     ;Run next command
PR8:call    EvalExpr                    ;Evaluate the expression
    push    ecx
    call    DispNumber                  ;Print the value
    pop     ecx
    jmp     PR3                         ;More to print ?
;----------------------------------------------------------------------------------------------
;'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'   
.data
    GosLineNum          DD 0            ;Line number of previous GOSUB or 0 if not
    GosPtrTxt           DD 0            ;Text pointer of previous GOSUB or 0
    GosStackPtr         DD 0            ;GOSUB Stack pointer
.code
GOSUB:
    call    EvalExpr                    ;Evaluate expression following GOSUB (could be replaced by TstNum)
    push    edx                         ;Save text pointer 
    call    FindLine                    ;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    
@@: cmp     GosLineNum, 0               ;Other GOSUB processing ?
    je      GS1                         ;No, update GosLineNum & GosPtrTxt
    mov     ebx, GosStackPtr            ;Yes, room enough in Gosub stack ?
    add     ebx, 8
    .IF ebx <= TxtStart
        jmp     @F
    .ENDIF
    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
@@: 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. adress 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
    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
RETURN:
    mov     ebx, GosLineNum             ;Does a GOSUB exist ?
    or      ebx, ebx 
    jnz     @F                          ;Yes, OK
    mov     edi, OFFSET Err12           ;No, error <RETURN without GOSUB>
    jmp     DispError                   
@@: mov     LineNumber, ebx             ;Update corresponding GOSUB line number 
    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                              
@@: 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                     ;Et on continue l'exécution à la suite du 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 with EXP3 = 1 

; Mini-Basic will find the variable adress 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.
; Mini-Basic 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.
FFOR:
    cmp     ForAdrVar, 0                ;Another FOR..NEXT active loop ?
    jz      @F                          ;No
    call    ForPushStack                ;Yes, save parameters of this loop in stack
@@: call    VarEquExpr                  ;Evaluate Variable=EXPR
    mov     ForAdrVar, ebx              ;Variable adress in ForAdrVar
    mov     ebx, TableTO-1              ;Execute keyword 'TO'
    jmp     Execute                    
FR1:call    EvalExpr                    ;Evaluate expression after 'TO' (come back here after execute)
    mov     ForLimit, ebx               ;and save it
    mov     ebx, TableSTEP-1            ;Execute keyword STEP
    jmp     Execute                    
FR2:call    EvalExpr                    ;Evaluate STEP expression (come back here)
    jmp     @F                         
FR3:mov     ebx, 1                      ;(or here if STEP not found) STEP not found, incrément = 1
@@: mov     ForIncrement, ebx           ;Save increment
    mov     ebx, LineNumber             ;Save current line
    mov     ForLine, ebx     
    mov     ForPtrTxt, edx              ;Save text pointer (end of FOR..NEXT Command)
                        ; Check if FOR variable is already used in another active FOR..NEXT loop         
    mov     edx, ForAdrVar              ;edx = adress 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                      
@@: add     ebx, 20                     ;    No, loop to previous FOR..NEXT stack area
    jmp     FR4
FR5:mov     edx,ForPtrTxt               ;No identical variable found
    jmp    NextCmd                     ;continue execution beyond FOR..TO..(STEP)
;----------------------------------------------------------------------------------------------- 
; 'NEXT' can be followed by VAR or not. Either way, Mini-Basic 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.
NEXT:
    call    TestVar                     ;Variable after NEXT? Yes, ebx=variable adress, No, ebx=0
L2: push    edx                         ;Save text pointer
    mov     edx, ebx                    ;edx = NEXT adress variable
    mov     ebx, ForAdrVar              ;ebx = FOR adress variable
    or      ebx, ebx                    ;= 0 ?
    jnz     @F                          ;No, continue 
    mov     edi, OFFSET Err15           ;Yes, no corresponding FOR, Error <NEXT without FOR>
    jmp     DispError
@@: or      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 does'nt match corresponding FOR> 
    jmp     DispError  
@@: 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     edx,ForPtrTxt               ;and text pointer
    jmp     NextCmd                     ;and continue execution after FOR command
    ;Update FOR..NEXT stack and pointer if other active loops.                                         
@@: 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  
@@: jmp     NextCmd
;-----------------------------------------------------------------------------------------------
; Save FOR loop parameters in ForNextStack
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>
@@: xchg    eax, esp                    ;esp at the top of stack area 
    push    ForAdrVar                   ;Save 5 parameters of FOR..NEXT loop  
    push    ForPtrTxt      
    push    ForLine      
    push    ForLimit
    push    ForIncrement
    xchg    eax, esp                    ;Restore esp
    mov     ForStackPtr, eax            ;update FOR..NEXT stack pointer 
    ret
;-----------------------------------------------------------------------------------------------
;'REM' command does'nt 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
REM:
    xor     ebx, ebx                    ;To avoid FindNextLine to search a line number
    jmp     IFR    
;-----------------------------------------------------------------------------------------------
;'IF' command evaluate following expression. If boolean result is TRUE, execute rest of current
; line. If result is FALSE, skip the rest of line and execute next line. 
; THEN command. does'nt exist in Mini-basic
IFF:
    call    EvalExpr
    or      ebx,ebx                     ;Boolean result of expression = 0 (FALSE) ?
    jz      IFR                         ;Yes, skip end of line & execute next line
    jmp     ContLine                    ;No, execute rest of line
IFR:call    FindEndOfLine  
    jc      @F                          ;IF end of Basic text, jump to Prompt
    jmp     ExecLine                    ;else execute next line
@@: jmp     Ready
;-----------------------------------------------------------------------------------------------
;'INPUT' waits for a 32 bits signed value entry (with [positive] or negative sign).
; If an error occurs, input screen area is cleared an 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' 
INPUT: ;
    call    TestVar                     ;Test variable following INPUT
    jnc     @F                          ;Valid variable (ebx = variable adress)
E19:mov     edi, OFFSET Err19           ;Else, error <INPUT variable expected>
    jmp     DispError 
@@: push    ebx                         ;save variable adress
    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, 10              ;Line input length limited to higher 32 bits number
    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     ah,"+"                      ;First char is '+' ?
    call    SkipSpaces                  ;if yes, skip and ignore this char.
    mov     wtemp1, 0                   ;Flag for negative value (0 = +, 1 = -)
    mov     ah,"-"                      ;First char is '-' ?
    call    SkipSpaces
    jne     @F                          ;No, flag = 0
    mov     wtemp1,1                    ;Yes, flag = 1
@@: call    TstNum                      ;Input value in ebx
    mov     ah,0DH                      ;end with CR ?
    call    SkipSpaces
    pop     ecx    
    je      @F                          ;Yes, continue
    mov     DWORD PTR Csbi_CursorPositionX, ecx ;No, restore input cursor position
    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]," "
    mov     DWORD PTR Csbi_CursorPositionX, ecx ;Restore again input cursor position               ;
    jmp     IP1                         ;and user try again
@@: pop     edx                         ;Restore basic text pointer
    pop     eax                         ;Testore variable adress
    cmp     wtemp1, 0                   ;If negative value, Change sign 
    je      @F
    call    ChangeSign
@@: mov     [eax], ebx
    jmp     NextCmd
;----------------------------------------------------------------------------------------------
; CrOrLet test end of line. If found execute next line, else execute a variable assignation
CrOrLet:
    mov     al, [edx]
    cmp     al,0DH                      ;Is it an empty line ?
    jz      @F                          ;Yes, run next line
    call    VarEquExpr                  ;No, decode Variable=<EXPR> 
@@: jmp     NextCmd

;----------------------------------------------------------------------------------------------
; EvalExpr evaluates arithmetical or logical expressions.
; <EXPR> = <EXPR2> or <EXPR2><REL.OP><EXPR2>
;
; Where <REL.OP> is one of the operators in TAB8 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> = <EXPR4> (<* or />) <EXPR4>) (....)
; <EXPR4> = <VARIABLE> or <FUNCTION> or <EXPR>
;
; <EXPR> is recursive so that - variable '@' can have an  EXPR as index,
;                             - FUNCTIONS can have an <EXPR> as arguments, and
;                             - <EXPR4> can be an <EXPR> in parentheses
EvalExpr:                               ;EXPR1
    call    EXPR2                       ;<EXPR2> = (+ or -) <EXPR3> (+ or -) <EXPR3> (....)
    push    ebx
    mov     ebx, OFFSET TAB8-1          ;Lookup relational operator table
    jmp     Execute                     ;Go do it. If not found, go to X17

X11:call    X18                         ;Rel.Op '>='
    jc      @F                          ;False, bl = 0
    mov     bl,al                       ;True,  bl = 1
@@: ret

X12:call    X18                         ;Rel.Op '<>'
    jz      @F                          ;False, bl = 0
    mov     bl,al                       ;True,  bl = 1
@@: ret

X13:call    X18                         ;Rel.Op '>'
    jz      @F                          ;False, bl = 0
    jc      @F                          ;Also False, bl = 0
    mov     bl,al                       ;True,  bl = 1
@@: ret

X14:call    X18                         ;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
@@: ret

X15:call    X18                         ;Rel.Op '='
    jnz     @F                          ;False, bl = 0
    mov     bl,al                       ;True,  bl = 1
@@: ret
 
X16:call    X18                         ;Rel.Op '<'
    jnc     @F                          ;False, bl = 0
    mov     bl,al                       ;True,  bl = 1
@@: ret

X17:pop     ebx                         ;No Rel. Op found
    ret                                 ;Return ebx = <EXPR2>

X18: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
    mov     ebx,eax                     
    call    CompareSign                 ;Compare 1st. with second
    pop     edx
    mov     ebx,0                       
    mov     al,1                        
    ret

EXPR2:                                  ;<EXPR2> = (+ or -) <EXPR3> (+ or -) <EXPR3> (....)
    mov     ah,'-'
    call    SkipSpaces                  ;Negative sign before EXPR3 ?
    jnz     X21                         ;No
    mov     ebx,0                       ;Yes, fake '0-'
    jmp     X27                         ;Treat like substract
X21:mov     ah,'+'                      ;Positive sign before EXPR3 ?
    call    SkipSpaces                  ;'+' or nothing is same
X22:call    EXPR3                       ;1st <EXPR3> = <EXPR4> (<* or />) <EXPR4>) (....) 
X23:mov     ah,'+'
    call    SkipSpaces                  ;Add a new EXPR3 ?
    jnz     X26                         ;No, test '-'
    push    ebx                         ;Yes, save value
    call    EXPR3                       ;     Get 2nd <EXPR3>
X24:XCHG    edx,ebx                     ;     Result in edx
    pop     eax                         ;
    push    ebx
    mov     ebx,eax                     ;ebx <-> [SP] NOW, [SP]->Buffer, ebx = old EXPR3
    add     ebx,edx
    pop     edx
    jno     X23                         ;Continue if not overflow
X25:mov     edi, OFFSET Err2            ;else : Overflow error 
    jmp     DispError                  
X26:mov     ah,'-'
    call    SkipSpaces                  ;Substract a new EXPR3 ?
    jnz     R4                          ;No, Ret EXPR2
X27:push    ebx                         ;Yes, save previous <EXPR3>
    call    EXPR3                       ;     get 2nd <EXPR3> (= <EXPR4> (<* or />) <EXPR4>) (....))
    call    ChangeSign                  ;     change sign
    jmp     X24                         ;     and looks for a next '+' or '-'

EXPR3:                                  ;<EXPR3> = <EXPR4> (<* or />) <EXPR4>) (....)
    call    EXPR4                       ;Get 1st. <EXPR4> 
X31:mov     ah,'*'
    call    SkipSpaces                  ;Multiply?
    jnz     X34                         ;No, test divide
    push    ebx                         ;Yes, save 1st.
    call    EXPR4                       ;and get 2nd EXPR4>
    XCHG    edx,ebx                     ;2nd in edx now
    pop     eax                         
    push    ebx
    imul    eax,edx                     ;ax:=ax*dx
    jo      X25                         ;Overflow Error
    mov     ebx,eax                     ;Result in ebx
    jmp     X35                         ;Look for more
X34:mov     ah,'/'
    call    SkipSpaces                  ;Divide ?
    jnz     R4                          ;No, ret 
    push    ebx                         ;Yes, save 1st <EXPR4>
    call    EXPR4                       ;and get the second one
    XCHG    edx,ebx                     ;Put 2nd in edx
    pop     eax                         
    push    ebx
    mov     ebx,eax
    or      edx,edx                     ;edx = 0 ?
    jnz     @F                          ;No, OK
X32:mov     edi, OFFSET Err6            ;Yes, Error : Division by 0
    jmp     DispError
@@: call    Divide                      ;Divides ebx by edx. Result in ecx, Remainder in ebx
    mov     ebx,ecx                     ;Result in ebx
    mov     cx, 6                       ;Six spaces
X35:pop     edx                         ;Restore text pointer
    jmp     X31                         ;Look for more terms

EXPR4:                                  ;<EXPR4> = <VARIABLE> or <FUNCTION> or <EXPR>
    mov     ebx, OFFSET TAB4-1          ;Find function in TAB4
    jmp     Execute                     ;And got do it
X40:call    TestVar                     ;No, not a function
    jc      X41                         ;Nor a variable
    mov     eax,[ebx]                   ;Variable
    mov     ebx, eax
R4: ret
X41:call    TstNum                      ;Or is it a number ?
    mov     al,ch                       ;Number of digits
    or      al,al
    jnz     R4                          ;OK, it's a number
TestParenth:
    mov     ah,'('
    call    SkipSpaces                  ;Not a number, must be an (expr)
    jnz     @F                          ;Not a "("
    call    EvalExpr                    ;"(" found, evaluate EXPR
@@: mov     ah,')'                      ;followed by a ")"
    call    SkipSpaces                  ;OK, ret
    jz      @F 
    Mov     edi, OFFSET Err0            ;else error <Syntax error>
    jmp     DispError   
@@: ret
;-----------------------------------------------------------------------------------------------
;'RND(expr)' = (cycle_counter*dl MOD EXPR)+1 
RND:
    call    TestParenth                 ;Evaluate (EXPR)
    test    ebx,ebx
    jns     @F                          ;Must be positive
    jnz     @F                          ;And > zero
    jmp     E18                         ;Else Error <Bad parameter>
@@: 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
    inc     ebx
    ret
;-----------------------------------------------------------------------------------------------
;'ABS(EXPR)' returns absolute value of EXPR
ABS:
    call    TestParenth
    call    CheckSign                   ;Invert sign if negative
    or      eax,ebx
    jns     R5                          ;OK, ret
    jmp     E18                         ;else error : <Invalid parameter>
;-----------------------------------------------------------------------------------------------
;'SIZE' returns in ebx amount of free Basic text memory
SIZZE:
    push    edx      
    mov     edx,TxtEnd                  ;ebx = bytes between 'TxtEnd' and AZVar pointers
    mov     ebx, LineNum       
    sub     ebx,edx
    pop     edx
R5: ret
;-----------------------------------------------------------------------------------------------
;'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) 
GET:
    call    TestVar                     ;GET followed by a variable ?
    jnc     @F                          ;Yes, ebx = variable adress
    jmp     E19                         ;No, error <Variable expected>
@@: push    edx                         ;save text pointer
    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
@@: invoke  FlushConsoleInputBuffer, hStdIn  ;Clear keyboard input buffer  
    xor     bl, 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                         ;become 225 to 254
    cmp     dl, 88
    ja      GT1
    add     dl, 166
@@: mov     bl,dl
GT1:mov     [edi],ebx                   ;result is placed in variable adress
    pop     edx
    jmp     NextCmd                     ;and execution continues
;-----------------------------------------------------------------------------------------------
;'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
POKE:
    call    EvalExpr
    push    ebx
    call    FindComma   
    jz      @F
    jmp     E17                         ;Not found, error <',' expected>
@@: call    EvalExpr
    cmp     ebx,255
    ja      E2                          ;Yes, error <Numeric value overflow>   
    mov     al,bl
    pop     ebx
    cmp     ebx, MEMSIZE                ;Inside Basic text memory ?
    jae     E2                          ;No, error <Numeric value overflow>
    mov  ecx, ebx
    add     ecx, TxtStart
    mov     [ecx],al
    inc     ebx
    push    ebx
    call    FindComma                   ;Other comma ?
    jz      @B                          ;Yes, loop back
PK0:pop     ebx                         ;No, next command                            
    jmp     NextCmd
;-----------------------------------------------------------------------------------------------
;'PEEK(EXPR) reads a 8 bits value located in adress EXPR located in Basic text area
PEEK:
    call    TestParenth                 ;Evaluate (EXPR)
    cmp     ebx, MEMSIZE                ;Inside Basic text memory ?
    jae     E2                          ;No, error <Numeric value overflow>
    add     ebx, TxtStart
    xor     eax, eax
    mov     al,[ebx]
    mov     ebx, eax                    ;ebx = 8 bits value 
    ret
;-----------------------------------------------------------------------------------------------
;'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
LOCATE:
    call    EvalExpr                    ;Evaluate X expression (column)
    cmp     bx, Csbi_MaximumWindowSizeX ;X < Window Size X ?
    jc      @F                          ;Yes, continue
E18:mov     edi, OFFSET Err18           ;No, error <Bad parameter> 
    jmp     DispError                   
@@: mov     si, bx                      ;Save column in si
    call    FindComma                   ;Check ','
    jz      @F                          ;Found, continue
E17:mov     edi, OFFSET Err17           ;Not found, error <',' expected>
    jmp     DispError 
@@: call    EvalExpr                    ;Evaluate Y expression (line)
    cmp     bx,Csbi_MaximumWindowSizeY  ;y < Window size Y ?
    jc      @F                          ;Yes, continue
    jmp     E18                         ;No, error
@@: mov     di,bx                       ;save line in di
    call    FindComma                   ;Check optional ',' for cursor visible or not
    jnz     LC0                         ;Not found, end of command
    call    EvalExpr                    ;Found, evaluate expression
    Push    edx
    or      ebx,ebx                     ;(EXPR) = 0 ?
    jz      @F                          ;Yes, hide cursor
    mov     bl,1                        ;No, set cursor
@@: mov     dtemp1, 18                  ;Cursor size if set
    mov     dtemp2,ebx
    invoke  SetConsoleCursorInfo, hStdOut, ADDR dtemp1
    call    TestApiError
    pop     edx
LC0:push    edx
    mov     Csbi_CursorPositionX, si    ;update new cursor position
    mov     Csbi_CursorPositionY, di
LC1:invoke  SetConsoleCursorPosition, hStdOut, DWORD PTR Csbi_CursorPositionX 
    call    TestApiError
    pop     edx
    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)
COLOR:
    call    EvalExpr                    ;Evaluate X expression (foreground color)
    cmp     ebx, 16                     ;Foreground color > 15 ?
    jc      @F                          ;no, continue
    jmp     E18                         ;Yes error <Bad parameter>
@@: push    ebx                         ;save foreground color
    call    FindComma                   ;Check ','
    jnz     E17                         ;Not found, error <',' expected>
    call    EvalExpr                    ;Evaluate Y expression (background color)
    cmp     ebx,16                      ;background color > 15 ?
    jc      @F                          ;No, continue
    jmp     E18                         ;Yes, error <Bad parameter>
@@: mov     eax, ebx                       
    sal     al,4                        ;background color in 4 left bits of al
    pop     ebx                         ;Restore foreground color
    add     al,bl                       ;Foreground color in 4 right bits  
    mov     ColorsCurrent, eax          ;Update current color
    jmp     NextCmd
;-----------------------------------------------------------------------------------------------
;'CLS' fills screen with spaces and colors attributes
CLS:
    push    edx
    call    WE1
    pop     edx
    jmp     NextCmd
;-----------------------------------------------------------------------------------------------
;'WAIT'(EXPR) waits for (EXPR) milliseconds then continue execution 
WWAIT:
    call    EvalExpr                    ;Evaluate expression
    call    WaitLoop                    ;and wait for ebx milliseconds
    jmp     NextCmd
;-----------------------------------------------------------------------------------------------
;'BIP' emits a 'beep' in internal loudspeaker or a standard Window alert sound if a sound card
; connected.
BIP:
    push    edx
    call    Beeep
    pop     edx
    jmp     NextCmd    
;-----------------------------------------------------------------------------------------------
; Tests event presence in console input buffer.
; In case of event, jumps to TestKeyb, else return to caller
TestInpBuf:
    push    edx
    invoke  GetNumberOfConsoleInputEvents, hStdIn, ADDR dtemp1
    call    TestApiError
    mov     ebx, dtemp1
    cmp     ebx, 0                      ;Events in input buffer ? 
    jne     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
TestKeyb: ; [registers used ecx, eax (API call)]
    push    edx
TK1:xor     ebx, ebx                     ;bx = keyscan number or 0   
    invoke  ReadConsoleInputA, hStdIn, ADDR EventBuffer, 1, ADDR dtemp1
    call    TestApiError
    cmp     DWORD PTR dtemp1, 0          ;Events in input buffer ? 
    je      @F                           ;No, return to caller    
    cmp     WORD PTR EventBuffer, 1      ;Yes, Is a Keyboard Event ?
    jne     @F                           ;no : return
    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
@@: 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.
LineInput:                              ;Event loop
    call    TestKeyb                    ;Keyboard event ?
    cmp     btemp1, 0                   ;Release key = 0 ?
    jz      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                  
@@: mov     bl, btemp1                  ;    Yes, 
    jmp     DispChar                    ;    and print char
KR1:or      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     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
KR2:cmp     bl, 224                     ;Cursor key flag ?
    jnz     @F                          ;No, test others cases
    mov     bl, EventBuffer+12          ;Yes, bl = key scan code
    cmp     bl, 71                      ;     = Home ?
    je      EL7
    cmp     bl, 79                      ;     = end ?
    je      EL6
    cmp     bl, 75                      ;     = Left arrow ?                    
    je      EL1
    cmp     bl, 77                      ;     = Right arrow ?
    je      EL2                     
    cmp     bl, 82                      ;     = Insert ?
    je      EL3             
    cmp     bl, 83                      ;     = Delete ?
    je      EL5
@@: 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.
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                             
    mov     si,-1                       ;flag for End Of Line routine
    call    EL6                         ;Use END key routine to find end of line cursor position
    xor     si,si
    call    Affiche_CrLf
    mov     btemp2, 0                   ;Reset Atl+num flag 
    pop     ebx
    ret                                 ;End of LineInput. Return to caller

EL1:dec     LinePtr                     ;[LEFT ARROW]
    jnz     @F
    inc     LinePtr                     ;1st. char. Impossible to go left
    jmp     LineInput
@@: 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

EL2: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
 
EL3:xor     InsertMode, 1               ;[INSERT] Invert InsertMode bit 1     
    mov     BYTE PTR dtemp2, 1          ;Visible cursor 
    mov     dtemp1, 100                 ;Default cursor value = biggest size (insert mode)
    cmp     InsertMode, 1
    jz      @F
    mov     dtemp1, 20                  ;Normal mode size
@@: invoke  SetConsoleCursorInfo, hStdOut, ADDR dtemp1  ;Update cursor size    
    jmp     LineInput
   
EL5:mov     eax, LineLen                ;[DELETE]
    cmp     eax, LinePtr                ;Line Length = LinePtr ?
    jne     @F                          ;No, we can cancel 1 char.
    jmp     LineInput                   ;Yes, we do nothing
@@: 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
    mov     cx, 0
    inc     Csbi_CursorPositionY
@@: mov     Csbi_CursorPositionX, cx    ;Csbi_CursorPositionX-Y = 1 position right (or CR-LF)  
    invoke  ReadConsoleOutputCharacterA, hStdOut, edi, ebx, DWORD PTR Csbi_CursorPositionX, ADDR dtemp1
    call    TestApiError ;Read console char. to move & update LineBuf buffer        
    pop     DWORD PTR Csbi_CursorPositionX
    push    edi
    pop     edi
    invoke  WriteConsoleOutputCharacterA, hStdOut, edi, ebx, DWORD PTR Csbi_CursorPositionX, ADDR dtemp1 
    call    TestApiError                ;Write char. previously read 1 position right
    dec     LineLen
    jmp     LineInput

EL6: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
    mov     ax, 0
    inc     bx
    jmp     @B
@@: cmp     si,-1                      ;Call from CR key ?
    jne     @F                         ;No
    mov     Csbi_CursorPositionY, bx   ;Update end of line Y cursor position before Affiche_CrLf
    ret                                ;Ret to CR key routine
@@: mov     LinePtr, ecx 
    jmp     ICP

EL7: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

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  ReadConsoleOutputCharacterA, 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  WriteConsoleOutputCharacterA, hStdOut, edi, ebx, DWORD PTR Csbi_CursorPositionX, ADDR dtemp1 
    call    TestApiError                ;Write char. previously read 1 position right
    dec     LineLen
    dec     LinePtr
    jmp     InputCP
;-----------------------------------------------------------------------------------------------
DispChar: ; Display a char in line editor.
          ; Manage auto-insert, CR-LF and scroll. Update cursor position
    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
@@: 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  ReadConsoleOutputCharacterA, 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
    mov     cx, 0
    inc     Csbi_CursorPositionY
@@: mov     Csbi_CursorPositionX, cx    ;Csbi_CursorPositionX-Y = 1 position right (or CR-LF)  
    invoke  WriteConsoleOutputCharacterA, 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
RC1:inc     LineLen
RC2:invoke  WriteConsoleA, 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
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
Divide:  ; [register used ecx, eax]
    push    edx                         ;Preserve edx
    mov     ecx, edx
    xor     edx,edx
    mov     eax,ebx
    idiv    ecx
    mov     ecx,eax                     ;Quotient
    mov     ebx,edx                     ;Remainder
    pop     edx 
    ret
;-----------------------------------------------------------------------------------------------
; 'CheckSign' checks sign of ebx. If +, no change. If -, change sign and invert sign of ch
CheckSign:
    or      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
;-----------------------------------------------------------------------------------------------;----------------------------------------------------------------------------------------------
;'CompareSign' Check sign of ebx and edx. If different, ebx and edx are interchanged, if same sign,
; not interchanged. Either case, ebx and edx are compared to set flags
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
    jns     @F                          ;Yes, only compare
    xchg    edx,ebx                     ;No, exchange and compare    
@@: cmp     ebx,edx
    ret
;-----------------------------------------------------------------------------------------------;----------------------------------------------------------------------------------------------
; 'VarEquExpr' expects a variable, followed by an equal sign and then an expression
; Evaluates the expression and sets the variable to the value
VarEquExpr:
    call    TestVar                     ;A variable is found ?
    jnc     @F                          ;Yes, search sign '='
    mov     edi, OFFSET Err8            ;No, error <Keyword or variable expected>
    jmp     DispError
@@: push    ebx                         ;Save adress of variable
    mov     ah,'='
    call    SkipSpaces                  ;Sign '=' found ?
    jz      @F                          ;Yes, evaluate expression            
    mov     edi, OFFSET Err9            ;No, error <Sign '=' expected>
    jmp     DispError
@@: call    EvalExpr
    mov     ecx,ebx                     ;Value in ecx now
    pop     ebx                         ;restore variable adress
    mov     [ebx],ecx                   ;Save value in variable adress
    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
CmdEnd:
    mov     ah,':'                      ;Found ':' ?
    call    SkipSpaces
    jnz     @F                          ;no, try CR
    pop     eax                         ;Yes, Remove ret adress from stack
    jmp     ContLine                    ;     and continue execution on the same line
@@: mov     ah,0DH                      ;Found a CR ?
    call    SkipSpaces
    jnz      @F                         ;No, test REM       
    pop     eax                         ;Yes, remove ret adress from stack
    jmp     NextLine                    ;and run next line
@@: mov     ah, "'"                     ;Found a REM ?
    call    SkipSpaces
    jnz     @F                          ;No, return to the caller
    pop     eax                         ;Yes, skip end of line
    jmp     REM
@@: ret     
;-----------------------------------------------------------------------------------------------;----------------------------------------------------------------------------------------------
;'TestCR' checks if a command is ended with a CR. Required for some commands without
; arguments (RETURN, STOP,...)
TestCR:
    mov     ah,0DH                      ;end with CR ?
    call    SkipSpaces
    jz      @F                          ;Yes, return to caller, else error 
    mov     edi, OFFSET Err0            ;No, Error : <Syntax error>
    jmp     DispError
@@: ret
;----------------------------------------------------------------------------------------------
.data
    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 Expr.",0
    Err6     db "Division by 0",0
    Err7     db "':' or end of line expected",0
    Err8     db "Keyword or variable expected",0
    Err9     db "Sign '=' 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 does'nt match corresponding FOR",0
    Err17    db "',' expected",0
    Err18    db "Invalid parameter",0
    Err19    db "Variable expected",0          
    Err20    db "File not found",0
    Err21    db "File too large",0
    Err22    db "Not a Mini Basic file",0
.code

DispError:
    push    edi                         ;save offset of error message
    cmp     Csbi_CursorPositionX, 0     ;Message must be printed at top of line
    je      @F
    call    Affiche_CrLf
@@: mov     edx, OFFSET ErrorTxt
    xor     al,al                       ;Message end delimiter
    call    DispStr                     ;Display 'error'
    cmp     LineNumber,0                ;Line number to print ?
    jz      @F                          ;No, Affiche_CrLf & return to 'Ready'
    mov     edx,OFFSET ErrorIn          ;Yes, print ' in '
    xor     al,al     
    call    DispStr    
    mov     cl,0                        ;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    Affiche_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
;
; 'FindNextLign' looks for End of line (0dh) from the line begining and jump to LineNumCompare.
; 'FindEndOfLine' Does the same but start searching after line number.

FindLine:
    or      ebx,ebx                     ;Line number Sign bit must be = 0
    JNS     @F                          ;Yes, OK
    mov     edi, OFFSET Err1            ;No, error : Invalid line number 
    jmp     DispError    
@@: 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      FindNextLign                ;no, loop to following line number in Basic text 
@@: ret                                 ;Yes, line number found : NC & Z; not found : NC &,NZ

FindNextLign:
    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 IFF END OF TEXT
;-----------------------------------------------------------------------------------------------          
;'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]
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, 0dh                    ;Is it a CR ?
    je      @F                         ;Yes, end too
    mov     [ebx], ah                  ;None of above cases, add char. in print buffer
    inc     ebx
    inc     edx
    jmp     PS1                        ;transfer loop back
@@: push    edx
    sub     edx, edi                    ; edx = Number of char. to print
    jz      PS3                         ;No char. to print
    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  WriteConsoleOutputCharacterA, 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    Affiche_CrLf
PS3:pop     edx
    inc     edx
    ret   
;-----------------------------------------------------------------------------------------------
; Print a string between quotes.
; Perform a CR without LF if '\' is found
QuotedString:
    mov     ah,34                       ;Next char. is a (")                                  "
    call    SkipSpaces
    jnz     QS1                         ;No, look for a '\' 
    mov     al,34                       ;Yes, print following chars. until another (")        "
    call    DispStr                     
    cmp     al,0DH                      ;Was last char. a CR ?
    pop     ebx                         ;remove return adress from stack
    jnz     @F                          ;No, return to caller + jump instruction
    jmp     NextLine                    ;Yes, end of line, execute next line
@@: add     ebx, 2                      ;Skips 2 bytes (jump instruction following call QuotedString)
    jmp     ebx                         ;and jump to this adress (after jump instruction)
QS1:mov     ah,'\'            
    call    SkipSpaces                  ;Is it a Back-Slash ? ('\')
    jnz     @F                          ;No, normal return
    mov     Csbi_CursorPositionX, 0     ;Yes, perform a CR without LF
    invoke  SetConsoleCursorPosition, hStdOut, DWORD PTR Csbi_CursorPositionX
    pop     ebx                         ;remove return adress from stack
    jmp     @B                          ;Return to caller + jump instruction
@@: ret                                 ;No (\), no (")                                        " 
;-----------------------------------------------------------------------------------------------
; On entry, ebx = binary number, cl = spaces
; '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
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
    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
    or      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
@@: mov     edi,OFFSET dtemp1           ;Buffer to print the number
    pop     ecx                         ;Now, all digits are in the stack
@@: dec     cl                          ;Dec space counter
    or      cl,cl                       ;Leading blanks ? 
    JS      @F                          ;No, We can print number
    mov     al,32                       ;Yes, we print a space
    stosb                               ;in [edi]    
    jmp     @B
@@: 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
@@: xor     al,al
    mov     [edi],al                    ;Display number
    push    edx
    mov     edx, OFFSET dtemp1
    call    DispStr
    pop     edx
    ret
;-----------------------------------------------------------------------------------------------
; Display line number & Basic text line
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
@@: xor     al,al                       ;And then the text line
    call    DispStr
    add     ebp, edi
R7: ret
;-----------------------------------------------------------------------------------------------
;'MoveUp' moves a block up from adress edx to adress ecx until edx = ebx
MoveUp:
    cmp     edx,ebx                     ;if edx = ebx : Return to caller
    jz      @F    
    mov     al,[edx]
    mov     [ecx],al
    inc     edx
    inc     ecx
    jmp     MoveUp                      ;Loop back until done
;-----------------------------------------------------------------------------------------------
;'MoveDown' moves a block down from adress edx to adress ebx until edx = ecx
MouvDown:
    cmp     edx,ecx                     ;Idem MoveUp
    jz      @F    
    dec     edx
    dec     ebx
    mov     al,[edx]
    mov     [ebx],al 
    jmp     MouvDown  
@@: ret
;-----------------------------------------------------------------------------------------------
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]=" "?
    jnz     @F                         ;No, Test ah before end of loop
    inc     edx                        ;Yes, loop to next char.
    jmp     SkipSpaces                 
@@: inc     edx                        ;dx+1 if ah test OK
    cmp     al,ah                      ;[dx]=ah ?
    jz      @F                         ;Yes, Return with edx+1, pointing on next char.
    dec     edx                        ;No , edx points on ah char.
@@: ret
;-----------------------------------------------------------------------------------------------
;'NextCmd' test end of current command. if next char. is ':' or CR, execution continues,
; else error : <':' or end of line expected>
NextCmd:
    call    CmdEnd                      ;If ':' or CR found, execution continues without return
    mov     edi, OFFSET Err7            ;If a ret occur from CmdEnd, Error.
    jmp     DispError
;-----------------------------------------------------------------------------------------------
;'DLOAD' 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 Mini-Basic 'SAVE' command
DLOAD:
    call    SkipSpaces
    call    SaveFileTitle  
    invoke  CreateFile, ADDR FileTitle, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
    cmp     eax,INVALID_HANDLE_VALUE    ;File does'nt exist ?
    jnz     @F                          ;No, continue
E20:mov     edi, OFFSET Err20           ;Yes <File not found>
    jmp     DispError
@@: 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    
@@: push    eax                         ;Save file length
    invoke  ReadFile, FileHandle, ADDR dtemp1, 4, ADDR dtemp2, 0
    call    TestApiError                ;read header to be sure file comes from Mini basic
    cmp     dtemp1, 987654321           ;Very secret file signature
    je      @F
    invoke  CloseHandle, FileHandle
    mov     edi, OFFSET Err22           ;No match, error <Not a Mini Basic file>
    jmp     DispError        
@@: 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
    pop     eax                         ;Basic text length
    mov     ebx, TxtStart               ;Update end of text pointer
    add     ebx, eax
    mov     TxtEnd, ebx
    jmp     Ready
;-----------------------------------------------------------------------------------------------
;'DSAVE' saves a Mini-Basic text file with signature header
DSAVE:
    mov     eax, TxtEnd                 ;Is there code to save ? 
    sub     eax, TxtStart
    or      eax, eax
    jz      Ready                       ;No 
    push    eax                         ;Yes, save code lenght
    call    SkipSpaces                  
    call    SaveFileTitle  
    invoke  CreateFile, ADDR FileTitle, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
    call    TestApiError                ;Open file to write in 
    mov     FileHandle, eax
    mov     dtemp1, 987654321           ;(secret!) header to dentify Mini Basic file 
    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
;-----------------------------------------------------------------------------------------------
; Prepares a title string with '.MBI' extension and '0' delimiter for DLOAD & DSAVE commands
SaveFileTitle:
    xor     ah,ah                       ;Char. counter
    mov     ebx, OFFSET FileTitle
@@: mov     al,[edx]
    cmp     al,0Dh
    jz      @F
    mov     [ebx],al
    inc     ebx
    inc     edx
    inc     ah
    cmp     ah, 8                       ;Maximum 8 char. for file title. 
    je      @F                          ;Further char. are troncated
    jmp     @B
@@: mov     eax, FileExt
    mov     [ebx], eax                  ;File extension '.MBI'
    add     ebx,4
    mov     BYTE PTR [ebx], 0           ;End of string delimiter
    ret
;-----------------------------------------------------------------------------------------------
; This subroutine is called after each API call and displays 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
TestApiError:
    mov     DWORD PTR MsgBoxText1, "IPA "
    cmp     eax, 0
    jz      Error 
    ret
;------------------------------------------------------------------------------------------------    
Error:
.data
MsgBoxCaption            DB "API Error",0 
MsgBoxText               DB "Error " 
MsgBoxText1              DB "    ", 0 
.code
    invoke  MessageBoxA, 0, ADDR MsgBoxText, ADDR MsgBoxCaption, 0 
    invoke  ExitProcess, 0
;----------------------------------------------------------------------------------------------
; See 'BIP' routine
Beeep:
    invoke  Beep, 440, 250        ;440 Hz. 250 Msec. (or standard Windows alert sound)
    ret 
;----------------------------------------------------------------------------------------------- 
; Performs a CR+LF vith eventual scroll up and update cursor position
Affiche_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
@@: inc     Csbi_CursorPositionY
CR1:invoke  SetConsoleCursorPosition, hStdOut, DWORD PTR Csbi_CursorPositionX ;Update cursor position
    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
RedimWindow:
    invoke  GetConsoleScreenBufferInfo, hStdOut, ADDR Csbi_SizeX
    invoke  GetLargestConsoleWindowSize, hStdOut
    mov     DWORD PTR wtemp1, eax
    cmp     si, wtemp1                  ;requested width > maximum allowed ?
    jae     E18                         ;Yes, error <Bad parameter>
    cmp     di, wtemp2                  ;Same for height
    jae     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      RD3                         ;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
    call    TestApiError
    invoke  SetConsoleScreenBufferSize, hStdOut, DWORD PTR Csbi_SizeX
    call    TestApiError
    ;invoke  GetLastError
    jmp     RD1
    ;Current buffer is smaller, redim buffer first, then window 
@@: invoke  SetConsoleScreenBufferSize, hStdOut, DWORD PTR Csbi_SizeX ;Buffer plus petit, 
    call    TestApiError
    invoke SetConsoleWindowInfo, hStdOut, 1, ADDR Csbi_WindowLeft  ;on fait l'inverse
    call    TestApiError
RD1:mov     ConsoleCharNbr, ebx         ;Save new console screen char. numbers
RD3:invoke  GetConsoleScreenBufferInfo, hStdOut, ADDR Csbi_SizeX; update Csbi buffer
    call    TestApiError
    mov     eax, 0                          ;Nothing allowed in input
    invoke  SetConsoleMode, hStdOut, eax
    call    TestApiError
    mov     eax, ENABLE_PROCESSED_INPUT      ;Ctrl-C is used to leave Mini-Basic, mouse ignored   
    invoke  SetConsoleMode, hStdIn, eax
    call    TestApiError
    call    WE1                              ;Set screen attributes
    ret    
;------------------------------------------------------------------------------------------------
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  ScrollConsoleScreenBufferA, hStdOut, ADDR SMALL_RECT_Left, 0, 0, ADDR wtemp1
    call    TestApiError
    pop     eax
    ret
;-----------------------------------------------------------------------------------------------
;'WaitLoop' waits for ebx milliseconds and returns to caller
WaitLoop:
    pushad
    xor     dx, dx                               ;millisec. counter
    call    GetTime
    mov     ax, WORD PTR dtemp1+12               ;save current Second (Reference)
    mov     cx, WORD PTR dtemp1+14               ;save current Millisecond (reference)
WL1:call    GetTime
    cmp     WORD PTR dtemp1+12, ax               ;second + 1 ?         
    je      @F                                   ;No, count milliseconds
    mov     ax, WORD PTR dtemp1+12               ;Yes, second ref = current sec
    mov     cx, 0                                ;     millis. ref = 0 
@@: mov     si, WORD PTR dtemp1+14               
    sub     WORD PTR dtemp1+14, cx               ;dtemp1+14 = millisec. since last GetTime
    mov     cx, si                               ;cx = new millisec. ref.
    add     dx, WORD PTR dtemp1+14               ;add millisec. nbr. between 2 GetTime 
    cmp     dx, bx                               ;over requested delay ?
    jb      WL1                                  ;No, count again
    popad                                        ;Yes, return 
    ret
;-----------------------------------------------------------------------------------------------
GetTime:
    push    cx
    push    dx
    push    ax
    invoke  GetLocalTime,ADDR dtemp1
    pop     ax
    pop     dx
    pop     cx
    ret
;-----------------------------------------------------------------------------------------------
end start  
 
 
 
