; =============================================================================
;
;                           Litos8 system functions
;
; =============================================================================

; -----------------------------------------------------------------------------
; Exported user functions (16):
;	SysGetInt - get current interrupt handler address
;	SysSetInt - set new interrupt handler address
;	SysGetInt32 - get current interrupt handler address 32-bit
;	SysSetInt32 - set new interrupt handler address 32-bit
;	TokenUp - uppercase token
;	Tokenize - tokenize text
;	InputText - input text from user (till Enter)
;	GetStdOut - get StdOut file (standard output)
;	GetStdIn - get StdIn file (standard input)
;	StdOutTextStr - output TEXT to StdOut file
;	ExecCmd - execute command
;	Command - execute command shell interpreter
;	KernelCheck - check system integrity
;	GetCommandLine - get command line
;	SysVer - get system version
;	Exit - return to system from application
; -----------------------------------------------------------------------------

		CODE_SECTION

; -----------------------------------------------------------------------------
;                    Execute command shell interpreter
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT with command to execute (NULL = prompt user)
; NOTES:	Destroys all registers, does not return and initializes memory.
; -----------------------------------------------------------------------------

; ------------- check system integrity

Exit:		call	KernelCheck	; check system integrity

; ------------- exit from application

		cli			; disable interrupt
		xor	cx,cx		; CX <- 0
		mov	ss,cx		; SS <- 0
		mov	sp,API_ADDR	; SP <- end of stack
		mov	ds,cx		; DS <- 0
		mov	es,cx		; ES <- 0
		cld			; set direction UP
		call	MemInit		; initialize memory manager
		call	KeyReinit	; reinitialize keyboard driver

		mov	ax,Exit		; AX <- exit from application
		push	ax
		push	ax		; emergency return to system

		sti			; enable interrupts

; ------------- initialize standard input and output

Command:	call	StdInit		; init StdIn and StdOut

; ------------- display prompt

		mov	al,CR		; AL <- CR char
		call	DispConChar	; move cursor to start of line
		call	GetCurPath	; get current path -> BX
		call	DispConTextStr	; display current path
		mov	al,">"		; AL <- prompt character
		call	DispConChar	; display prompt

; ------------- input from user

		mov	di,CommandLine	; DI <- command line buffer
		mov	cx,COMMAND_MAX	; CX <- max. length of command line
		mov	ax,[CommandLineLast] ; AX <- old text length
		call	InputText	; input text from user
		mov	[CommandLineLast],ax ; store text length
		xchg	ax,cx		; CX <- new length of text

; ------------- execute command

		mov	si,di		; SI <- command line
		call	ExecCmd		; execute command
		jnc	short Command	; command found OK

; ------------- error - command not found

		mov	bx,CmdNotFound	; BX <- error text
		call	DispConTextStr	; display error text
		jmp	short Command	; next command

; -----------------------------------------------------------------------------
;                              Execute command
; -----------------------------------------------------------------------------
; INPUT:	CX = length of text (max. 128 chars)
;		SI = buffer with text
; OUTPUT:	CY = error, command not found
; -----------------------------------------------------------------------------

; ------------- push registers

ExecCmd:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP

; ------------- limit text length

		cmp	cx,COMMAND_MAX	; check command length
		jbe	short ExecCmd1	; command length is OK
		mov	cx,COMMAND_MAX	; CX <- limit command length

; ------------- tokenize text

ExecCmd1:	mov	di,TokenLine	; DI <- token buffer
		call	Tokenize	; tokenize text
		mov	[TokenLineLast],ax ; last number of tokens
		or	ax,ax		; any token?
		jz	short ExecCmd3	; no token
		dec	ax		; skip first token
		xchg	ax,bp		; BP <- number of tokens - 1

; ------------- uppercase first token

		mov	si,di		; SI <- first token
		call	TokenUp		; uppercase token

; ------------- prepare to find token

		mov	di,CmdTab	; DI <- internal command table
		lodsb			; AL <- token length
		cbw			; AX <- token length
		xchg	ax,cx		; CX <- token length

; ------------- change disk A: ...

		cmp	cl,2		; length = 2 ?
		jne	short ExecCmd4	; no
		mov	ax,[si]		; AX <- load 2 characters
		cmp	ah,":"		; is second character ":" ?
		jne	short ExecCmd4	; no

		mov	bx,TokenLine	; BX <- buffer
		mov	[bx+TEXT_Text],ax ; prepare text with path

		sub	al,"A"		; AL <- required disk
		push	bx		; push BX
		call	SDiskGetDesc	; get system disk descriptor -> BX
		pop	bx		; pop BX
		jnc	short ExecCmd2	; disk is OK

		mov	bx,CmdDiskTxt1	; BX <- text "Invalid disk"
		call	DispConTextStr	; display error text
		jmp	short ExecCmd3

ExecCmd2:	mov	word [bx+TEXT_Length],3 ; set text length
		mov	byte [bx+TEXT_Text+2],PATHCHAR ; set path separator
		call	SetCurPath	; set current path
ExecCmd3:	jmp	ExecCmd92

; ------------- check token length

ExecCmd4:	mov	al,[di]		; AL <- table token length
		inc	di		; DI <- increase pointer
		cbw			; AX <- table token length
		cmp	ax,cx		; length suits?
		jne	short ExecCmd5	; different length

; ------------- compare token text

		push	si		; push SI
		push	di		; push DI
		push	cx		; push CX

		repe	cmpsb		; compare token

		pop	cx		; pop CX
		pop	di		; pop DI
		pop	si		; pop SI
		jne	short ExecCmd5	; not this token
		jmp	ExecCmd8	; token has been found

; ------------- next token

ExecCmd5:	add	di,ax		; DI <- skip token text
		inc	di
		inc	di		; DI <- skip function address
		cmp	byte [di],0	; end of table
		jne	short ExecCmd4	; search next token

; ------------- token not found - execute external command

	; prepare filename text
		call	TextNewBuf	; new text
		jc	short ExecCmd90	; memory error
		xchg	ax,bx		; BX <- TEXT
		mov	ax,ExecExt	; AX <- L8X extension
		call	TextAddText	; add extension

	; open file
		mov	di,CmdFile	; DI <- command FILE structure
		call	OpenFile	; open file
		pushf			; push flags
		call	TextFree	; delete text
		popf			; pop flags
		jc	short ExecCmd90	; command not found

	; check file size
		cmp	word [di+FILE_Size+2],byte 0 ; check file size HIGH
		jnz	short ExecCmd96	; invalid file size
		call	MemMaxFree	; get max. memory
		mov	cx,[di+FILE_Size] ; CX <- file size
		cmp	ax,cx		; check file size
		jb	short ExecCmd90	; invalid file size

	; read file
		mov	si,EXEADDR	; execute address
		call	ReadFile	; read file
		pushf			; push flags
		call	CloseFile	; close file
		popf			; pop flags
		jc	short ExecCmd90	; load error

	; check execute head
		cmp	word [EXEADDR+EXEHEAD_Ident],"L8" ; check ident
		jne	short ExecCmd96	; error
		cmp	word [EXEADDR+EXEHEAD_Addr],EXEADDR ; check address
		jne	short ExecCmd96	; error
		cmp	cx,[EXEADDR+EXEHEAD_Size] ; check size
		jne	short ExecCmd96	; error
		add	ax,EXEADDR	; AX <- max. end address
		cmp	ax,[EXEADDR+EXEHEAD_End] ; check end address
ExecCmd90:	jb	short ExecCmd9	; error

	; check sum
		sub	cx,EXEHEAD_CRCBeg
		xor	dx,dx
		mov	si,EXEADDR+EXEHEAD_CRCBeg
ExecCmd6:	lodsb
		xor	dl,al
		rol	dx,1
		loop	ExecCmd6
		cmp	[EXEADDR+EXEHEAD_CRC],dx
ExecCmd96:	stc			; set error flag
		jne	short ExecCmd9	; error

	; initialize stack and memory and jump to program
		call	MemInit		; initialize memory manager

		xchg	ax,cx		; AX <- file size
		add	ax,3ffh		; round up
		mov	cl,10
		shr	ax,cl		; AX <- size in KB		
		mov	bx,si		; BX <- program address
		add	al,MEM_KB_BIAS	; AL <- block size
		call	MemMark		; mark block as used

		mov	sp,API_ADDR	; SP <- end of stack

		mov	ax,Exit		; AX <- exit from application
		push	ax
		push	ax		; emergency return to system

		call	GetCommandLine	; get command line
		jmp	[EXEADDR+EXEHEAD_Start] ; run program

; ------------- token found - execute internal command

ExecCmd8:	add	si,cx		; skip text of first token
		add	di,cx		; skip text of table token
		call	word [di]	; execute internal command

; ------------- pop registers

ExecCmd92:	clc			; clear error flag
ExecCmd9:	pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                              Uppercase token
; -----------------------------------------------------------------------------
; INPUT:	SI = token (1 byte length + N bytes text)
; -----------------------------------------------------------------------------

TokenUp:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

		lodsb			; AL <- token length
		cbw			; AX <- token length
		xchg	ax,cx		; CX <- token length
		jcxz	TokenUp4	; no text

TokenUp2:	lodsb			; AL <- load character
		call	UpperCase	; uppercase character
		mov	[si-1],al	; set new character
		loop	TokenUp2	; next character

TokenUp4:	pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                              Tokenize text
; -----------------------------------------------------------------------------
; INPUT:	CX = length of text (should be max. 128)
;		SI = buffer with text
;		DI = destinaton buffer for tokens (min. size CX + 1)
; OUTPUT:	AX = number of tokens
; NOTES:	Text is packed into tokens: 1 byte length + N bytes text
;		Quotation and apostrophe marks are removed.
; -----------------------------------------------------------------------------

; ------------- push registers

Tokenize:	push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP

; ------------- prepare registers

		xor	bp,bp		; BP <- 0, token counter
		add	cx,si		; CX <- end of text

; ------------- find start of next token

Tokenize2:	cmp	si,cx		; end of text?
		jae	short Tokenize9	; end of text
		lodsb			; AL <- load next character
		cmp	al," "		; is it space character?
		jbe	short Tokenize2 ; skip space character

; ------------- start of next token

		mov	bx,di		; BX <- start of token
		mov	byte [bx],0	; reset token length
		inc	di		; DI <- start of token text
		inc	bp		; increase token counter

; ------------- quotes

		cmp	al,QUOTATION	; quotation mark?
		je	short Tokenize3	; quotation mark
		cmp	al,APOSTROPHE	; apostrophe mark?
		jne	short Tokenize6	; no quotes

Tokenize3:	mov	ah,al		; AH <- quote mark
Tokenize4:	cmp	si,cx		; end of text?
		jae	short Tokenize9	; end of text
		lodsb			; AL <- load next character
		cmp	al,ah		; end quote mark?
		jne	short Tokenize5	; no quote mark
		cmp	si,cx		; was it last character?
		jae	short Tokenize5	; it was last charter
		cmp	byte [si],ah	; doubled quote mark?
		jne	short Tokenize2	; no doubled quote mark -> next token
		inc	si		; skip doubled quote mark
Tokenize5:	stosb			; store valid character
		inc	byte [bx]	; increase token length
		jmp	short Tokenize4	; parse next character

; ------------- token

Tokenize6:	stosb			; store character
		inc	byte [bx]	; increase token length
		cmp	si,cx		; end of text?
		jae	short Tokenize9	; end of text
		lodsb			; AL <- load next character
		cmp	al," "		; is it space character?
		ja	short Tokenize6	; it is valid character, store it
		jmp	short Tokenize2	; parse next token

; ------------- pop registers

Tokenize9:	xchg	ax,bp		; AX <- number of tokens
		pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                     Input text from user (till Enter)
; -----------------------------------------------------------------------------
; INPUT:	AX = old length of text
;		CX = max. length of text
;		DI = destination buffer
; OUTPUT:	AX = new length of text in buffer
; -----------------------------------------------------------------------------

; ------------- push registers

InputText:	push	bx		; push BX
		push	si		; push SI
		xchg	ax,si		; SI <- old length of text
		xor	bx,bx		; BX <- text offset

; ------------- wait for a kay

InputText2:	call	GetKeyWait	; get key from keyboard

; ------------- Left: move cursor left

		cmp	ah,KEY_LEFT	; left?
		jne	short InputText3 ; no
		mov	al,BS		; AL <- back space
		jmp	short InputText7 ; move cursor left

; ------------- Right: move cursor right

InputText3:	cmp	ah,KEY_RIGHT	; right?
		jne	short InputText4 ; no
		cmp	bx,si		; is there some old text?
		jae	short InputText2 ; no old text
		mov	al,[di+bx]	; AL <- old text
		jmp	short InputText8 ; restore character

; ------------- Home: move to start of text

InputText4:	cmp	ah,KEY_HOME	; Home?
		jne	short InputText5 ; no

		or	bx,bx		; any character in buffer?
		jz	short InputText2 ; already start of text
		mov	al,BS		; AL <- back space
InputText42:	call	DispConChar	; shift cursor left
		call	DispConSpc	; clear character
		call	DispConChar	; shift cursor left
		dec	bx		; decrease text size
		jnz	short InputText42 ; next character
InputText22:	jmp	short InputText2

; ------------- End: move to end of text

InputText5:	cmp	ah,KEY_END	; End?
		jne	short InputText6 ; no

InputText52:	cmp	bx,si		; is there some old text?
		jae	short InputText2 ; no old text
		cmp	bx,cx		; check max. length of text
		jae	short InputText2 ; buffer is full
		mov	al,[di+bx]	; AL <- old text
		inc	bx		; increase length of text
		call	DispConChar	; display character
		jmp	short InputText52 ; next character

; ------------- remap key to a character

InputText6:	call	CharFromKey	; get character from key
		or	al,al		; is it valid character?
		jz	short InputText22 ; invalid character

; ------------- TAB: ignore (we don't know previous position)

		cmp	al,TAB		; Tabulator?
		je	short InputText22 ; ignore tabulator

; ------------- FF: ignore

		cmp	al,FF		; FormFeed?
		je	short InputText22 ; ignore FormFeed

; ------------- LF: ignore

		cmp	al,LF		; LineFeed?
		je	short InputText22 ; ignore LineFeed

; ------------- Enter: end of text

		cmp	al,CR		; Enter?
		je	short InputText9 ; end of text

; ------------- BS: move cursor left

		cmp	al,BS		; BackSpace?
		jne	short InputText8 ; no BS
InputText7:	or	bx,bx		; any character in buffer?
		jz	short InputText22 ; already start of text
		dec	bx		; decrease text size
		call	DispConChar	; shift cursor left
		call	DispConSpc	; clear character
		call	DispConChar	; shift cursor left
		jmp	short InputText22

; ------------- character

InputText8:	cmp	bx,cx		; check max. length of text
		jae	short InputText22 ; buffer is full
		mov	[di+bx],al	; store character to buffer
		inc	bx		; increase length of text
		call	DispConChar	; display character
		cmp	bx,si		; check old text
		jbe	short InputText22
		mov	si,bx		; SI <- new maximum
		jmp	short InputText22

; ------------- display new line

InputText9:	call	DispConNewLine	; display new line

; ------------- pop registers

		xchg	ax,bx		; AX <- new length of text
		pop	si		; pop SI
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                     Internal command - VER (version)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

CmdVER:		call	SysVer		; get sytem version
		call	DispConTextStr	; display text
		call	DispConNewLine	; display new line
		ret

; -----------------------------------------------------------------------------
;                       Internal command - DIR
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

; ------------- no parameter

CmdDIR:		xor	cx,cx		; CX <- empty text
		or	bp,bp		; any parameter?
		jz	short CmdDir0	; no parameter

; ------------- create copy of token -> BX

		mov	cl,[si]		; CL <- token size
		inc	si		; SI <- start of text
CmdDir0:	call	TextNewBuf	; create new text -> AX
		jc	short CmdDir24	; error
		xchg	ax,bx		; BX <- text
		jcxz	CmdDir12	; add *.*

; ------------- get 2 entries

		xor	dx,dx		; DX <- attributes, all files
		mov	di,TokenLine	; DI <- buffer
		mov	bp,2		; BP <- limit
		call	FindFiles	; find files
		jc	short CmdDir1	; nothing found
		cmp	bp,1		; 1 entry?
		jne	short CmdDir2	; no		

; ------------- check if it is device or directory

		test	byte [di+FILE_Attr],DIR_Device+DIR_DIR ; device or dir?
		jz	short CmdDir2	; no, it is file

; ------------- add *.*

CmdDir1:	call	TextAddPathSep	; add path separator
CmdDir12:	mov	ax,CmdDirAll	; AX <- text *.*
		call	TextAddText	; add text *.*

; ------------- get number of files

CmdDir2:     	xor	dx,dx		; DX <- attributes, all files
		xor	di,di		; DI <- NULL, no destination array
		mov	bp,256		; BP <- limit
		call	FindFiles	; find files
		jc	short CmdDir22	; nothing found

; ------------- create destination buffer

		mov	ax,FILE_size	; AX <- size of FILE structure
		mul	bp		; AX <- required size of buffer
		call	MemAlloc	; get memory block
		jc	short CmdDir22	; error
		xchg	ax,di		; DI <- memory block

; ------------- load files list

		call	FindFiles	; find files

CmdDir22:	pushf			; push flags
		call	TextFree	; delete text
		popf			; pop flags
CmdDir24:	jc	short CmdDirErr	; error

		or	bp,bp		; check number of files
		jz	short CmdDir9	; nothing found

; ------------- push registers

		push	di		; push DI (memory block)
		push	bp		; push BP (number of entries)

; ------------- date and time

CmdDir3:	call	FileDateTime	; get file date and time
		mov	bh,al		; BH <- second
		mov	ah,DATEFORM_JAPAN ; japan format
		call	DispConDate	; display date
		call	DispConSpc	; display space char
		call	DispConTime	; display time
		call	DispConSpc	; display space char

; ------------- attributes

		call	CmdDirAttr	; display attributes

; ------------- size

		call	TextNewEmpty	; get new empty text -> AX
		jc	short CmdDir6	; memory error
		xchg	ax,bx		; BX <- text

		test	byte [di+FILE_Attr],DIR_DIR ; directory?
		jz	short CmdDir52	; not directory
		mov	ax,CmdDirDir	; AX <- "<DIR>" text
		call	TextAddText	; add text
		jmp	short CmdDir54		

CmdDir52:	call	FileSize	; get file size -> DX:AX
		call	TextAddDWord	; convert number to TEXT

CmdDir54:	mov	cx,10+1		; CX <- max. number of digits
		sub	cx,[bx+TEXT_Length] ; CX <- number of spaces
CmdDir5:	call	DispConSpc	; display space
		loop	CmdDir5		; next space

		call	DispConTextStr	; display file size
		call	TextFree	; delete text	
		call	DispConSpc	; display space char

; ------------- file name

CmdDir6:	call	TextNewEmpty	; get new empty text -> AX
		jc	short CmdDir7	; memory error
		xchg	ax,bx		; BX <- text

		mov	si,di		; SI <- file
		call	AddFileName	; add file name
		call	DispConTextStr	; display file name
		call	TextFree	; delete text

; ------------- next file

CmdDir7:	call	DispConNewLine	; display new line
		add	di,byte FILE_size ; DI <- shift to next file
		dec	bp		; BP <- file counter
		jnz	short CmdDir3	; next file
		
; ------------- pop registers

		pop	ax		; pop AX (number of entries)
		pop	di		; pop DI (memory block)

; ------------- display number of bytes and total size

		call	CmdDirSum	; display total size

; ------------- delete memory block

CmdDir9:	xchg	ax,di		; AX <- memory block
		call	MemFree		; free memory block
		ret

; ------------- nothing found

CmdDirErr:	mov	bx,CmdDirNFnd	; BX <- text "Not found"
		call	DispConTextStr	; display error text
		ret

; ============= attributes

CmdDirAttr:	call	FileAttr	; get file attributes -> AL
		mov	ah,al		; AH <- attributes

		mov	al,"A"		; "ARC"
		test	ah,DIR_ARC	; Archive?
		jnz	short CmdDirAttr2 ; yes
		mov	al,"-"		; no
CmdDirAttr2:	call	DispConChar	; display attributes "Archive"

		mov	al,"S"		; "SYS"
		test	ah,DIR_SYS	; System?
		jnz	short CmdDirAttr4 ; yes
		mov	al,"-"		; no
CmdDirAttr4:	call	DispConChar	; display attributes "System"

		mov	al,"H"		; "HID"
		test	ah,DIR_HID	; Hidden?
		jnz	short CmdDirAttr5 ; yes
		mov	al,"-"		; no
CmdDirAttr5:	call	DispConChar	; display attributes "Hidden"

		mov	al,"R"		; "R/O"
		test	ah,DIR_RO	; Read-only?
		jnz	short CmdDirAttr6 ; yes
		mov	al,"-"		; no
CmdDirAttr6:	call	DispConChar	; display attributes "Read-only"
		ret

; ============= display number of bytes and total size

CmdDirSum:	call	DispConWord	; display number of files
		xchg	ax,cx		; CX <- number of files
		mov	bx,CmdDirFiles	; BX <- text " files with "
		call	DispConTextStr	; display text

		xor	ax,ax		; AX <- 0
		xor	dx,dx		; DX <- 0
		mov	si,di		; SI <- buffer
CmdDirSum8:	add	ax,[si+FILE_Size] ; AX <- add file size LOW
		adc	dx,[si+FILE_Size+2] ; DX <- add file size HIGH
		add	si,FILE_size	; SI <- next entry
		loop	CmdDirSum8	; next entry

		call	DispConDWord	; display total size
		mov	bx,CmdDirFiles2	; BX <- text " bytes"
		call	DispConTextStr	; display text
		ret

; -----------------------------------------------------------------------------
;                     Internal command - CD (change directory)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

; ------------- no parameter, only display current path

CmdCD:		or	bp,bp		; any parameter?
		jnz	short CmdCD2	; yes
		mov	bx,CurPath	; BX <- current path
		call	DispConTextStr	; display current path
		call	DispConNewLine	; display new line
		ret

; ------------- set new current path (we may destroy token buffer)

CmdCD2:		lea	bx,[si-1]	; BX <- TEXT
		lodsb			; AL <- text length
		cbw			; AX <- text length
		mov	[bx+TEXT_Length],ax ; set text length
CmdCD3:		call	SetCurPath	; set new current path
		jnc	short CmdCD4	; path changed OK

		mov	bx,CmdCDErr	; BX <- text "Invalid path"
		call	DispConTextStr	; display error text
CmdCDThis:
CmdCD4:		ret

; ------------- up dir

CmdCDUp:	mov	bx,CmdCDUpPath	; BX <- up path
		jmp	short CmdCD3

; -----------------------------------------------------------------------------
;                     Internal command - MD (make directory)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

CmdMD:		or	bp,bp		; any parameter?
		jz	short CmdMD4	; no

		lea	bx,[si-1]	; BX <- TEXT
		lodsb			; AL <- text length
		cbw			; AX <- text length
		mov	[bx+TEXT_Length],ax ; set text length
		call	CreateDir	; create directory
		jnc	short CmdMD4	; directory created OK

		mov	bx,CmdMDErr	; BX <- text "Cannot create directory"
		call	DispConTextStr	; display error text
CmdMD4:		ret

; -----------------------------------------------------------------------------
;                Internal command - RD (delete directory)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

CmdRD:		or	bp,bp		; any parameter?
		jz	short CmdRD4	; no

		lea	bx,[si-1]	; BX <- TEXT
		lodsb			; AL <- text length
		cbw			; AX <- text length
		mov	[bx+TEXT_Length],ax ; set text length
		call	DeleteDir	; delete
		jnc	short CmdRD4	; operation OK

		mov	bx,CmdRDErr	; BX <- text "Cannot delete directory"
		call	DispConTextStr	; display error text
CmdRD4:		ret

; -----------------------------------------------------------------------------
;                Internal command - DEL (delete file)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

CmdDel:		or	bp,bp		; any parameter?
		jz	short CmdDel4	; no

		lea	bx,[si-1]	; BX <- TEXT
		lodsb			; AL <- text length
		cbw			; AX <- text length
		mov	[bx+TEXT_Length],ax ; set text length
		call	DeleteFile	; delete
		jnc	short CmdDel4	; operation OK

		mov	bx,CmdDelErr	; BX <- text "Cannot delete file"
		call	DispConTextStr	; display error text
CmdDel4:	ret

; -----------------------------------------------------------------------------
;                Internal command - REN (rename file or directory)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

CmdRen:		cmp	bp,byte 2	; 2 parameters?
		jb	short CmdRen6	; no

		lea	bx,[si-1]	; BX <- TEXT
		lodsb			; AL <- text length
		cbw			; AX <- text length
		mov	[bx+TEXT_Length],ax ; set text length

		xchg	ax,si		; SI <- text length
		inc	si
		inc	si		; SI <- skip TEXT head
		add	si,bx		; SI <- next string
		lodsb			; AL <- text length
		cbw			; AX <- text length
		xchg	ax,cx		; CX <- text length
		call	TextNewBuf	; create new text -> AX
		jc	short CmdRen6	; memory error

		call	RenameFileDir	; rename file or directory
		pushf			; push flags
		xchg	ax,bx		; BX <- new text
		call	TextFree	; delete text
		popf			; pop flags
		jnc	short CmdRen8	; operation OK

CmdRen6:	mov	bx,CmdRenErr	; BX <- text "Cannot rename"
		call	DispConTextStr	; display error text
CmdRen8:	ret

; -----------------------------------------------------------------------------
;                Internal command - MOVE (move file)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

CmdMove:	cmp	bp,byte 2	; 2 parameters?
		jb	short CmdMove6	; no

		lea	bx,[si-1]	; BX <- TEXT
		lodsb			; AL <- text length
		cbw			; AX <- text length
		mov	[bx+TEXT_Length],ax ; set text length

		xchg	ax,si		; SI <- text length
		inc	si
		inc	si		; SI <- skip TEXT head
		add	si,bx		; SI <- next string
		lodsb			; AL <- text length
		cbw			; AX <- text length
		xchg	ax,cx		; CX <- text length
		call	TextNewBuf	; create new text -> AX
		jc	short CmdMove6	; memory error

		call	MoveFile	; move file
		pushf			; push flags
		xchg	ax,bx		; BX <- new text
		call	TextFree	; delete text
		popf			; pop flags
		jnc	short CmdMove8	; operation OK

CmdMove6:	mov	bx,CmdMoveErr	; BX <- text "Cannot move file"
		call	DispConTextStr	; display error text
CmdMove8:	ret

; -----------------------------------------------------------------------------
;                Internal command - FREE (free space on disk)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

CmdFree:	mov	al,[CurPath+TEXT_Text] ; AL <- current path disk
		sub	al,"A"		; AL <- current disk

		call	DiskFree	; get disk free space
		call	DispConDWord	; display free space

		xchg	ax,cx		; AX <- total size LOW
		mov	dx,bx		; DX <- total size HIGH

		mov	bx,CmdFreeMsg	; BX <- text " bytes free, total size "
		call	DispConTextStr	; display text

		call	DispConDWord	; display total size
		call	DispConNewLine	; display new line
		ret

; -----------------------------------------------------------------------------
;                Internal command - TYPE (type text)
; -----------------------------------------------------------------------------
; INPUT:	SI = first token (1 byte length + N bytes text)
;		BP = number of tokens
; DESTROYS:	May destroy all registers.
; -----------------------------------------------------------------------------

CmdType:	or	bp,bp		; any parameter?
		jz	short CmdType8	; no

		lea	bx,[si-1]	; BX <- TEXT
		lodsb			; AL <- text length
		cbw			; AX <- text length
		mov	[bx+TEXT_Length],ax ; set text length
		mov	di,CmdFile	; DI <- FILE descriptor
		call	OpenFile	; open file
		jc	short CmdType6	; error

CmdType2:	mov	si,TokenLine	; SI <- buffer
		mov	cx,COMMAND_MAX+1 ; CX <- buffer size
		call	ReadFile	; read file
		jc	short CmdType6	; error
		jcxz	CmdType4	; end of file
		call	DispConText	; display text
		jmp	short CmdType2		

CmdType4:	call	CloseFile	; close file
		ret

CmdType6:	mov	bx,CmdTypeErr	; BX <- text "Cannot type file"
		call	DispConTextStr	; display error text
CmdType8:	ret

; -----------------------------------------------------------------------------
;                  Initialize standard input and output
; -----------------------------------------------------------------------------

; ------------- push registers

StdInit:	push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- initialize files

		mov	si,ConDevName	; SI <- "con" console device name
		mov	cl,3		; CL <- length of device ame
		mov	di,StdOut	; DI <- StdOut file
		call	OpenDevice	; open StdOut device
		mov	di,StdIn	; DI <- StdIn file
		call	OpenDevice	; open StdIn device

; ------------- pop registers

		pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                       Get StdOut file (standard output)
; -----------------------------------------------------------------------------
; OUTPUT:	BX = pointer to FILE structure with StdOut file
; -----------------------------------------------------------------------------

GetStdOut:	mov	bx,StdOut	; BX <- StdOut file
		ret

; -----------------------------------------------------------------------------
;                       Get StdIn file (standard input)
; -----------------------------------------------------------------------------
; OUTPUT:	BX = pointer to FILE structure with StdIn file
; -----------------------------------------------------------------------------

GetStdIn:	mov	bx,StdIn	; BX <- StdIn file
		ret

; -----------------------------------------------------------------------------
;                     Output text TEXT to StdOut file
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

StdOutTextStr:	; !!!!!!!!!!!!!!!!!

		call	DispConTextStr
		ret

; -----------------------------------------------------------------------------
;                  Get current interrupt handler address
; -----------------------------------------------------------------------------
; INPUT:	CL = interrupt number (0..255)
; OUTPUT:	AX = current interrupt handler address
;		CY = segment has not default value, need use SysGetInt32
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

SysGetInt:	pushf			; push flags
		push	bx		; push BX
		cli			; disable interrupts

; ------------- get interrupt handler address

		mov	bh,0		; BH <- 0
		mov	bl,cl		; BX <- interrupt number
		shl	bx,1		; BX *= 2
		shl	bx,1		; BX = pointer to interrupt address
		mov	ax,[bx]		; AX <- address LOW
		cmp	word [bx+2],byte 0 ; default segment?

; ------------- pop registers

		pop	bx		; pop BX
		jne	short SysGetInt2 ; invalid segment
		popf			; pop flags (maybe enable interrupts)
		clc			; clear NC, segment is OK
		ret

; ------------- invalid segment

SysGetInt2:	popf			; pop flags (maybe enable interrupts)
		stc			; set CY, invalid segment
		ret

; -----------------------------------------------------------------------------
;                     Set new interrupt handler address
; -----------------------------------------------------------------------------
; INPUT:	AX = new interrupt handler address
;		CL = interrupt number (0..255)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

SysSetInt:	pushf			; push flags
		push	bx		; push BX
		cli			; disable interrupts

; ------------- wet interrupt handler address

		mov	bh,0		; BH <- 0
		mov	bl,cl		; BX <- interrupt number
		shl	bx,1		; BX *= 2
		shl	bx,1		; BX = pointer to interrupt address
		mov	[bx],ax		; set address LOW
		mov	[bx+2],cs	; set address HIGH - default CS

; ------------- pop registers

		pop	bx		; pop BX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;               Get current interrupt handler address 32-bit
; -----------------------------------------------------------------------------
; INPUT:	CL = interrupt number (0..255)
; OUTPUT:	DX:AX = current interrupt handler address
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

SysGetInt32:	pushf			; push flags
		push	bx		; push BX
		cli			; disable interrupts

; ------------- get interrupt handler address

		mov	bh,0		; BH <- 0
		mov	bl,cl		; BX <- interrupt number
		shl	bx,1		; BX *= 2
		shl	bx,1		; BX = pointer to interrupt address
		mov	ax,[bx]		; AX <- address LOW
		mov	dx,[bx+2]	; DX <- address HIGH

; ------------- pop registers

		pop	bx		; pop BX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;               Set new interrupt handler address 32-bit
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = new interrupt handler address
;		CL = interrupt number (0..255)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

SysSetInt32:	pushf			; push flags
		push	bx		; push BX
		cli			; disable interrupts

; ------------- get interrupt handler address

		mov	bh,0		; BH <- 0
		mov	bl,cl		; BX <- interrupt number
		shl	bx,1		; BX *= 2
		shl	bx,1		; BX = pointer to interrupt address
		mov	[bx],ax		; set address LOW
		mov	[bx+2],dx	; set address HIGH

; ------------- pop registers

		pop	bx		; pop BX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;                            Get command line
; -----------------------------------------------------------------------------
; OUTPUT:	CX = length of command line
;		DX = number of tokens
;		SI = command line
;		DI = token line
; -----------------------------------------------------------------------------

GetCommandLine:	mov	si,CommandLine	; SI <- command line
		mov	cx,[CommandLineLast] ; CX <- length of command line
		mov	di,TokenLine	; DI <- buffer of tokens
		mov	dx,[TokenLineLast] ; DX <- number of tokens
		ret

; -----------------------------------------------------------------------------
;                            Get system version
; -----------------------------------------------------------------------------
; OUTPUT:	AL = version major number
;		AH = version minor number
;		BX = version CTEXT
;		DX = system ID (SYSVER_ID)
;		SI = kernel info table KERNINFO
; -----------------------------------------------------------------------------

SysVer:		mov	ax,SYSVER_MIN*256 + SYSVER_MAJ ; AX <- version number
		mov	bx,SysVerTxt	; BX <- version CTEXT
		mov	dx,SYSVER_ID	; DX <- system ID
		mov	si,KernelInfoTab ; SI <- kernel info table KERNINFO
		ret

; -----------------------------------------------------------------------------
;                       Check system integrity
; -----------------------------------------------------------------------------
; NOTES:	Halts on kernel CRC error.
; -----------------------------------------------------------------------------

; ------------- push registers

KernelCheck:	push	ax		; push AX

; ------------- check API CRC

		call	GetAPICRC	; get API check sum
		cmp	ax,[API_CRC]	; check API check sum
		jne	short KernelCheck2 ; error

; ------------- check kernel CRC

		call	GetKernelCRC	; get kernel CRC
		cmp	ax,[KernelCRC]	; check kernel CRC
		je	short KernelCheck4 ; kernel is OK

; ------------- display error message

KernelCheck2:	call	DispClear	; clear display
		mov	bx,KernelCRCErr	; BX <- text
		call	DispConTextStr	; display text
		cli			; disable interrupts
KernelCheckHalt:jmp	short KernelCheckHalt ; system stop

; ------------- pop registers

KernelCheck4:	pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                       Calculate kernel check sum
; -----------------------------------------------------------------------------
; OUTPUT:	AX = kernel check sum
; -----------------------------------------------------------------------------

;-------------- push registers

GetKernelCRC:	push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- prepare registers

		mov	si,CodeStart	; SI <- kernel start
		mov	cx,ConstEnd	; CX <- kernel end
		sub	cx,si		; CX <- kernel size (constant part)
		xor	dx,dx		; DX <- CRC accumulator

; ------------- calculate CRC

GetKernelCRC2:	lodsb			; AL <- load 1 byte
		xor	dl,al		; XOR byte
		rol	dx,1		; DX <- rotate accumulator
		loop	GetKernelCRC2	; next byte
		xchg	ax,dx		; AX <- CRC

; ------------- pop registers

		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                            Constant data
; -----------------------------------------------------------------------------

		CONST_SECTION

CmdNotFound:	CTEXT	"Command not found!",13,10

ConDevName:	db	"con"		; console device name

ExecExt:	CTEXT	".L8X"		; program extension

KernelCRCErr:	CTEXT	"Broken integrity, system halted!"

; ------------- change disk command

CmdDiskTxt1:	CTEXT	"Invalid disk",13,10

; ------------- DIR command

CmdDirAll:	CTEXT	"*.*"
CmdDirNFnd:	CTEXT	"No files",13,10
CmdDirFiles:	CTEXT	" files with "
CmdDirFiles2:	CTEXT	" bytes",13,10
CmdDirDir:	CTEXT	"<DIR>"

; ------------- CD command

CmdCDErr:	CTEXT	"Invalid path",13,10
CmdCDUpPath:	CTEXT	".."

; ------------- MD command

CmdMDErr:	CTEXT	"Cannot create directory",13,10

; ------------- RD command

CmdRDErr:	CTEXT	"Cannot delete directory",13,10

; ------------- DEL command

CmdDelErr:	CTEXT	"Cannot delete file",13,10

; ------------- REN command

CmdRenErr:	CTEXT	"Cannot rename",13,10

; ------------- MOVE command

CmdMoveErr:	CTEXT	"Cannot move file",13,10

; ------------- FREE command

CmdFreeMsg:	CTEXT	" bytes free, used "

; ------------- TYPE command

CmdTypeErr:	CTEXT	"Cannot type file",13,10

; ------------- internal commands

CmdTab:		db	3,"VER"
		dw	CmdVER

		db	3,"DIR"
		dw	CmdDIR

		db	2,"CD"
		dw	CmdCD

		db	3,"CD."
		dw	CmdCDThis

		db	4,"CD.."
		dw	CmdCDUp

		db	2,"MD"
		dw	CmdMD

		db	2,"RD"
		dw	CmdRD

		db	3,"DEL"
		dw	CmdDel

		db	3,"REN"
		dw	CmdRen

		db	4,"MOVE"
		dw	CmdMove

		db	4,"FREE"
		dw	CmdFree

		db	4,"TYPE"
		dw	CmdType

		db	0

; ------------- system version

SysVerTxt:	CTEXT	"Litos8 v",SYSVER_TXT

; ------------- kernel info table KERNINFO

KernelInfoTab:	dw	CodeInitStart	; init code start
		dw	ConstInitStart	; init constant data start
		dw	CodeStart	; code start
		dw	ConstStart	; constant data start
		dw	DataStart	; data start
		dw	KernelEnd	; kernel end

; -----------------------------------------------------------------------------
;                            Uninitialised data
; -----------------------------------------------------------------------------

		DATA_SECTION

KernelCRC:	resw	1		; kernel check sum

; ------------- command line

TokenLine:	resb	COMMAND_MAX+1	; token line
TokenLineLast:	resw	1		; last number of tokens

CommandLine:	resb	COMMAND_MAX	; command line
CommandLineLast:resw	1		; last length of command line

CmdFile:	resb	FILE_size	; command FILE structure

; ------------- StdOut and StdIn files

StdOut:		resb	FILE_size	; standard output
StdIn:		resb	FILE_size	; standard input
