; Author: Pawel Jablonski
; E-mail: pj@xirx.net
; WWW: xirx.net
; GIT: git.xirx.net
;
; License: You can use this code however you like
; but leave information about the original author.
; Code is free for non-commercial and commercial use.

include lcd.asm
include string.asm
include speaker.asm
include fs.asm

; Command buffer
var commandBuffer[39]

; No arguments
; No return
global .xTerminal:
	; Reset
	call .xTerminalReset
	; Print start message
	call .xTerminalStartMessage
	; Play start sound
	call .xTerminalStartSound
	; Refresh screen
	call .xLcdRefresh
	
.xTerminal_loop:
	; Get command
	call .xTerminalGetCommand
	; Check command length
	mov low(*commandBuffer), X
	mov high(*commandBuffer), Y
	call .xStringLength
	; 0 == A
	cmp 0, A
	je .xTerminal_loop
	; Run app if command is not empty
	call .xTerminalRunApp
	; Get another command
	jmp .xTerminal_loop
	
; No arguments
; No return
external .xTerminalReset:
	push X
	push Y
	; Reset color
	call .xTerminalColorReset
	; Clear screen
	call .xLcdClear
	; Reset cursor position
	mov 0, X
	mov 0, Y
	call .xStringSetCursor
	pop Y
	pop X
	ret

; No arguments
; No return
external .xTerminalColorReset:
	push A
	; Change colors to default
	mov 0x0, A
	call .xLcdSetBackground
	mov 0xf, A
	call .xLcdSetForeground
	pop A
	ret

; Start message to show
const messageSystem "XiOS 0.1"
const messageFreeRom "32K Program memory free"
const messageFreeRam "28K Data memory free"

; No arguments
; No return
.xTerminalStartMessage:
	; Print name and system version
	mov low(*messageSystem), X
	mov high(*messageSystem), Y
	call .xStringPrintRom
	call .xStringNewLine
	call .xStringNewLine
	; Print information about ROM for apps
	mov low(*messageFreeRom), X
	mov high(*messageFreeRom), Y
	call .xStringPrintRom
	call .xStringNewLine
	; Print information about RAM for apps
	mov low(*messageFreeRam), X
	mov high(*messageFreeRam), Y
	call .xStringPrintRom
	ret

; No arguments
; No return
.xTerminalStartSound:
	push A
	; Set fill parameter
	mov 8, A
	call .xSpeakerSetFill
	; Set volume parameter
	mov 8, A
	call .xSpeakerSetVolume
	; Set time parameter
	mov 24, A
	call .xSpeakerSetTime
	; Play e6 tone
	mov 68, A
	call .xSpeakerSetNote
	call .xSpeakerAddNote
	; Play e5 tone
	mov 56, A
	call .xSpeakerSetNote
	call .xSpeakerAddNote
	pop A
	ret

; No arguments
; No return
.xTerminalGetCommand:
	; Reset color
	call .xTerminalColorReset
	; Show command line
	call .xStringNewLine
	call .xStringNewLine
	mov '>', A
	call .xStringPrintChar
	call .xLcdRefresh
	mov 39, A
	mov low(*commandBuffer), X
	mov high(*commandBuffer), Y
	call .xStringRead
	call .xStringNewLine
	ret

; Error messages for run app
const appNameLengthError "Error: Bad length of app name"
const appNameUnknownNameError "Error: Unknown app"

; Address of app argument string
var appArgX
var appArgY

; No arguments
; No return
.xTerminalRunApp:
	push A
	push X
	push Y
	; Load command buffer address
	mov low(*commandBuffer), X
	mov high(*commandBuffer), Y
	; Load first char
	ldr A
	; First char cannot be a space
	cmp ' ', A
	je .xTerminalRunApp_appNameLengthError
	; Find a space after command
	mov ' ', A
	call .xStringFind
	; If the space is not found then it is no arguments
	cmp 0, A
	je .xTerminalRunApp_no_arg
	; Move address at space after app name
	add A, X
	incc Y
	; Replace a space by end of string char
	str 0
	; Move address at begin of arguments
	inc X
	incc Y
	call .xStringLength
	; It is no arguments if string length is equal zero
	cmp 0, A
	je .xTerminalRunApp_no_arg
	jmp .xTerminalRunApp_checkAppName
	; Set arguments address for case where no argument was found
.xTerminalRunApp_no_arg:
	; Load command buffer address
	mov low(*commandBuffer), X
	mov high(*commandBuffer), Y
	call .xStringLength
	; Move address at end+1 of app name
	inc A
	add A, X
	incc Y
	; Store end of string char
	str 0
	; Check app name
.xTerminalRunApp_checkAppName:
	; Store arguments address to local variables
	str X, *appArgX
	str Y, *appArgY
	; Load command buffer address
	mov low(*commandBuffer), X
	mov high(*commandBuffer), Y
	; Check app name length
	call .xStringLength
	; If app name lenght is equal to zero then show error message
	cmp 0, A
	je .xTerminalRunApp_appNameLengthError
	; If app name lenght is greather than 8 then show error message
	cmp 8, A
	; 8 < A
	jl .xTerminalRunApp_appNameLengthError
	; Run load app procedure
	call .xTerminalLoadApp
	; If load app procedure returned flase then show error message
	cmp 0, A
	je .xTerminalRunApp_appUnknownNameError
	; Make a new line before app output
	call .xStringNewLine
	; Load args address
	ldr *appArgX, X
	ldr *appArgY, Y
	; Run app
	call 0x8000
	pop Y
	pop X
	pop A
	ret
	; Show length error message
.xTerminalRunApp_appNameLengthError:
	call .xStringNewLine
	mov low(*appNameLengthError), X
	mov high(*appNameLengthError), Y
	call .xStringPrintRom
	pop Y
	pop X
	pop A
	ret
	; Show unknow name of app error message
.xTerminalRunApp_appUnknownNameError:
	call .xStringNewLine
	mov low(*appNameUnknownNameError), X
	mov high(*appNameUnknownNameError), Y
	call .xStringPrintRom
	pop Y
	pop X
	pop A
	ret

; No arguments
; Return:
; 	A - success (0 - error, 1 - ok)
.xTerminalLoadApp:
	push B
	push X
	push Y
	; Set name of app
	mov low(*commandBuffer), X
	mov high(*commandBuffer), Y
	call .xFSSetName
	; Open app
	call .xFSOpenApp
	cmp 0, A
	je .xTerminalLoadApp_fail
	; Get app size and compare to max loadable size
	call .xFSSize
	; 0x80 < B
	cmp 0x80, B
	jl .xTerminalLoadApp_fail
	; Do not load empty file
	push B
	or A, B
	; 0 == B
	cmp 0, B
	pop B
	je .xTerminalLoadApp_fail
	; Calc how many block are needed to read
	dec A
	decc B
	; Set starting block
	mov 0, A
	; Set starting address
	mov 0x00, X
	mov 0x80, Y
	; Run copy loop B+1 times
.xTerminalLoadApp_loop:
	call .xFSReadBlock
	inc A
	inc Y
	loopz B, .xTerminalLoadApp_loop
	pop Y
	pop X
	pop B
	; Return true for success
	mov 1, A
	ret
	; Fail exit of load app
.xTerminalLoadApp_fail:
	pop Y
	pop X
	pop B
	; Return false for fail
	mov 0, A
	ret