	.include	"lib/global.h"

	.MAXCURSPOSX	= 0x13	; In tiles
	.MAXCURSPOSY	= 0x11

	.MINMSPOSX	= 0x02	; In tiles
	.MINMSPOSY	= 0x0A
	.MAXMSPOSX	= 0x11
	.MAXMSPOSY	= 0x0F
	.INIMSPOSX	= .MINMSPOSX
	.INIMSPOSY	= .MINMSPOSY

	.MSOFFSETX	= 0x0C	; In pixels
	.MSOFFSETY	= 0x14

	.MINACCEL	= 0x0800
	.MAXACCEL	= 0x0100

	.KBDWINPOSY	= 0x08	; In tiles
	.SCROLLSPD	= 0x0100
	.KBDSIZE	= 0x1006; In tiles (0x10 x 0x06)

	.BUFLEN		= 0x20

	.SPACE		= 0x20
	.BS		= 0x08
	.CR		= 0x0A	; Unix
;	.CR		= 0x0D	; Dos

	.area	HEADER	(ABS)

	.org	.MODE_TABLE+4*.T_MODE
	JP	.tmode

	.module	Terminal

	.area	_BSS

.curx:
	.ds	0x01
.cury:
	.ds	0x01
.msx:
	.ds	0x01
.msy:
	.ds	0x01
.msacc:
	.ds	0x02
.msstate:
	.ds	0x01
.mschanged:
	.ds	0x01
.string_len:
	.ds	0x01
.input_buffer:
	.ds	.BUFLEN

	.area	_CODE

	; Enter text mode
.tmode::
	LD	A,#.T_MODE
	LD	(.mode),A

	CALL	.wait_vbl	; Must be in VBL before turning the screen off.

	LD	A,#0b01000001	; LCD		= Off (no picture on screen)
	LDH	(.LCDCONT),A
				; WindowBank	=	0x9C00
				; Window	=	Off
				; BG Chr	=	0x8800
				; BG Bank	=	0x9800
				; OBJ		=	8x8
				; OBJ		=	Off
				; BG		=	On

	CALL	.std_pal	; Normal palette

	LD	BC,#.tp1
	LD	HL,#0x8000
	LD	DE,#.endtp1-.tp1
	CALL	.mv_tiles	; Move tiles

	LD	BC,#.tp3
	LD	HL,#0x8800
	LD	DE,#.endtp3-.tp3
	CALL	.mv_tiles	; Move tiles

	LD	BC,#.tp2
	LD	HL,#0x9000
	LD	DE,#.endtp2-.tp2
	CALL	.mv_tiles	; Move tiles

	XOR	A		; A = 0
	LD	(.curx),A
	LD	(.cury),A
	LD	A,#<.MINACCEL	; Acceleration
	LD	(.msacc),A
	LD	A,#>.MINACCEL
	LD	(.msacc+1),A

	; Initialize window
	LD	BC,#.frame_tiles
	LD	DE,#0x0000	; Place image at (0x00,0x00) tiles
	LD	HL,#0x140A	; Image size is 0x14 x 0x0A tiles
	CALL	.set_xy_wtt
	LD	BC,#.kbdtable
	LD	DE,#0x0202	; Place image at (0x02,0x02) tiles
	LD	HL,#.KBDSIZE	; Image size is 0x10 x 0x06 tiles
	CALL	.set_xy_wtt
	XOR	A		; A = 0
	LD	A,#.MINWNDPOSX
	LDH	(.WNDPOSX),A
	LD	A,#.MAXWNDPOSY	; Hide window
	LDH	(.WNDPOSY),A

	; Initialize sprite
	XOR	A		; A = 0
	LD	C,#0x00		; Sprite 0x00
	LD	D,#0x00		; Default sprite properties
	CALL	.prop_sprite
	LD	C,#0x00		; Sprite 0x00
	LD	D,#0x00		; Tile 0x00
	CALL	.set_sprite
	LD	A,#0b00101100
	LDH	(.SPR0PAL),A

	; Clear screen
	CALL	.cls

	LD	A,#0b11000001	; LCD Controller = On
	LDH	(.LCDCONT),A
	EI			; Enable interrupts

	RET

	; Prompt the user for a char and return it in A
.get_char:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	CALL	.show_kbd
	CALL	.show_mouse
1$:
	CALL	.track_mouse
	CALL	.update_mouse
	CALL	.jpad
	LD	D,A
	AND	#.A		; Is A pressed ?
	JP	Z,1$

	LD	A,(.msy)	; Look for char under the mouse
	SUB	#.MINMSPOSY
	JR	Z,12$
	LD	E,A
	XOR	A		; A = 0
11$:
	ADD	#.MAXMSPOSX-.MINMSPOSX+1
	DEC	E
	JR	NZ,11$
12$:
	LD	E,A
	LD	A,(.msx)
	SUB	#.MINMSPOSX
	ADD	E
	LD	HL,#.kbdtable
	LD	B,#0x00
	LD	C,A
	ADD	HL,BC
	LD	B,(HL)

	CALL	.hide_mouse
	CALL	.hide_kbd
	LD	A,B

	POP	HL
	POP	DE
	POP	BC
	RET

	; Prompt the user for a string and store it in the
	; address pointed by HL
.get_string:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	CALL	.show_kbd
	CALL	.show_bkg
	CALL	.show_mouse
	XOR	A		; A = 0
	LD	(.string_len),A
1$:
	CALL	.track_mouse
	CALL	.update_mouse
	CALL	.jpad
	LD	D,A
	AND	#.A		; Is A pressed ?
	JP	NZ,10$
	LD	A,D
	AND	#.B		; Is B pressed ?
	JP	NZ,20$
	LD	A,D
	AND	#.SELECT	; Is SELECT pressed ?
	JP	NZ,30$
	LD	A,D
	AND	#.START		; Is START pressed ?
	JR	Z,1$
	CALL	.padup		; Wait for button to be depressed

	LD	A,#.CR
	CALL	.put_char
	LD	(HL),#0x00
	CALL	.hide_mouse
	CALL	.hide_bkg
	CALL	.hide_kbd
	POP	HL
	POP	DE
	POP	BC
	RET

10$:
	; Insert a character at cursor position
	LD	A,(.string_len)	; Check buffer length
;	CP	#.BUFLEN-1	; Keep 1 char for EOS
;	JR	Z,13$
	INC	A
	LD	(.string_len),A	; Update it
	LD	A,(.msy)	; Look for char under the mouse
	SUB	#.MINMSPOSY
	JR	Z,12$
	LD	E,A
	XOR	A		; A = 0
11$:
	ADD	#.MAXMSPOSX-.MINMSPOSX+1
	DEC	E
	JR	NZ,11$
12$:
	LD	E,A
	LD	A,(.msx)
	SUB	#.MINMSPOSX
	ADD	E
	PUSH	HL
	LD	HL,#.kbdtable
	LD	B,#0x00
	LD	C,A
	ADD	HL,BC
	LD	A,(HL)
	POP	HL
	LD	(HL+),A		; Add it into input buffer
	CALL	.put_char	; Print it
	CALL	.show_bkg	; Ensure the text is not hidden
13$:
	CALL	.padup		; Wait for button to be depressed
	JP	1$

20$:
	; Delete a character at cursor position
	LD	A,(.string_len)	; Is there any char in the buffer ?
	OR	A
	JR	Z,21$
	DEC	A		; Yes
	LD	(.string_len),A	; Update buffer length
	DEC	HL
	CALL	.del_char
21$:
	CALL	.padup		; Wait for button to be depressed
	JP	1$

30$:
	CALL	.hide_mouse
	CALL	.hide_bkg
	CALL	.hide_kbd
	CALL	.padup		; Wait for button to be depressed
	CALL	.show_kbd
	CALL	.show_bkg
	CALL	.show_mouse
	JP	1$

.show_kbd:
	PUSH	BC
	PUSH	DE
	LDH	A,(.LCDCONT)
	OR	#0b00100000	; Window = On
	LDH	(.LCDCONT),A
	LD	A,#.MAXWNDPOSY	; Show window
1$:
	LDH	(.WNDPOSY),A
	CP	#.KBDWINPOSY*0x08
	JR	Z,99$
	DEC	A
	LD	D,A
	LD	BC,#.SCROLLSPD
	CALL	.delay_ns
	LD	A,D
	JR	1$
99$:
	POP	DE
	POP	BC
	RET

.hide_kbd:
	PUSH	BC
	PUSH	DE
	LD	A,#.KBDWINPOSY*0x08+1
1$:				; Hide window
	LDH	(.WNDPOSY),A
	CP	#.MAXWNDPOSY
	JR	Z,2$
	INC	A
	LD	D,A
	LD	BC,#.SCROLLSPD
	CALL	.delay_ns
	LD	A,D
	JR	1$
2$:
	LDH	A,(.LCDCONT)
	AND	A,#0b11011111	; Window = Off
	LDH	(.LCDCONT),A
	POP	DE
	POP	BC
	RET

.show_bkg:
	PUSH	BC
	PUSH	DE
	LDH	A,(.SCROLLY)
	LD	D,A
	LD	A,(.cury)
	SUB	#.KBDWINPOSY-1
	JR	C,99$
	JR	Z,99$
	SLA	A		; A = A * 8
	SLA	A
	SLA	A
	SUB	D
	JR	C,99$
	JR	Z,99$
	LD	E,A
	LDH	A,(.SCROLLY)
1$:
	INC	A
	LDH	(.SCROLLY),A
	DEC	E
	JR	Z,99$
	LD	D,A
	LD	BC,#.SCROLLSPD
	CALL	.delay_ns
	LD	A,D
	JR	1$
99$:
	POP	DE
	POP	BC
	RET

.hide_bkg:
	LDH	A,(.SCROLLY)
	OR	A
	RET	Z
	PUSH	BC
	PUSH	DE
1$:
	DEC	A
	LDH	(.SCROLLY),A
	JR	Z,99$
	LD	D,A
	LD	BC,#.SCROLLSPD
	CALL	.delay_ns
	LD	A,D
	JR	1$
99$:
	POP	DE
	POP	BC
	RET

.show_mouse:
	LD	A,#.INIMSPOSX
	LD	(.msx),A
	LD	A,#.INIMSPOSY
	LD	(.msy),A
	CALL	.set_mouse
	LDH	A,(.LCDCONT)
	OR	#0b00000010	; OBJ = On
	LDH	(.LCDCONT),A
	RET

.hide_mouse:
	LDH	A,(.LCDCONT)
	AND	#0b11111101	; OBJ = Off
	LDH	(.LCDCONT),A
	RET

.track_mouse:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	XOR	A		; A = 0
	LD	(.mschanged),A	; Default to no change
	CALL	.jpad
	LD	D,A

	LD	HL,#.msstate
	AND	#.UP+.DOWN+.LEFT+.RIGHT
	JR	NZ,1$
	LD	(HL),#0x00	; Reset state
	JP	99$
1$:
	LD	A,(HL)
	LD	(HL),#0x01	; Set state
	OR	A		; Was it 0 ?
	LD	HL,#.msacc	; Acceleration
	JR	NZ,2$
				; Yes
	LD	(HL),#<.MINACCEL
	INC	HL
	LD	(HL),#>.MINACCEL
	JR	4$		; Update position
2$:
	LD	C,(HL)
	INC	HL
	LD	B,(HL)
	DEC	BC
	LD	A,B
	OR	C
	JR	Z,3$
	LD	(HL),B
	DEC	HL
	LD	(HL),C
	JP	99$
3$:				; Set new acceleration to maximum
	LD	(HL),#>.MAXACCEL
	DEC	HL
	LD	(HL),#<.MAXACCEL
4$:				; Update position
	LD	A,#0x01
	LD	(.mschanged),A
	LD	A,D
	AND	#.UP		; Is UP pressed ?
	JR	Z,6$
	LD	A,(.msy)
	CP	#.MINMSPOSY
	JR	Z,5$
	DEC	A
	LD	(.msy),A
	JR	6$
5$:
	LD	A,#.MAXMSPOSY
	LD	(.msy),A
6$:
	LD	A,D
	AND	#.DOWN		; Is DOWN pressed ?
	JR	Z,8$
	LD	A,(.msy)
	CP	#.MAXMSPOSY
	JR	Z,7$
	INC	A
	LD	(.msy),A
	JR	8$
7$:
	LD	A,#.MINMSPOSY
	LD	(.msy),A
8$:
	LD	A,D
	AND	#.LEFT		; Is LEFT pressed ?
	JR	Z,10$
	LD	A,(.msx)
	CP	#.MINMSPOSX
	JR	Z,9$
	DEC	A
	LD	(.msx),A
	JR	10$
9$:
	LD	A,#.MAXMSPOSX
	LD	(.msx),A
10$:
	LD	A,D
	AND	#.RIGHT		; Is RIGHT pressed ?
	JR	Z,99$
	LD	A,(.msx)
	CP	#.MAXMSPOSX
	JR	Z,11$
	INC	A
	LD	(.msx),A
	JR	99$
11$:
	LD	A,#.MINMSPOSX
	LD	(.msx),A
99$:
	POP	HL
	POP	DE
	POP	BC
	RET

.update_mouse:
	LD	A,(.mschanged)	; Did it change ?
	OR	A
	RET	Z		; No
.set_mouse:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,#0x00		; Sprite 0x00
	LD	A,(.msx)
	SLA	A		; A = A * 8
	SLA	A
	SLA	A
	ADD	#.MSOFFSETX
	LD	D,A
	LD	A,(.msy)
	SLA	A		; A = A * 8
	SLA	A
	SLA	A
	ADD	#.MSOFFSETY
	LD	E,A
	CALL	.mv_sprite
	POP	HL
	POP	DE
	POP	BC
	RET

	; Print a character without interpretation
.out_char::
	CALL	.set_char
	CALL	.adv_curs
	RET

	; Print a character with interpretation
.put_char::
	CP	#.CR
	JR	NZ,1$
	CALL	.cr_curs
	RET
1$:
	CALL	.set_char
	CALL	.adv_curs
	RET

	; Delete a character
.del_char::
	CALL	.rew_curs
	LD	A,#.SPACE
	CALL	.set_char
	RET

	; Print the character in A
.set_char:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	E,A

	LD	A,(.cury)	; y coordinate
	LD	L,A
	LD	H,#0x00
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	LD	A,(.curx)	; x coordinate
	LD	C,A
	LD	B,#0x00
	ADD	HL,BC
	LD	BC,#0x9800
	ADD	HL,BC
1$:
	LDH	A,(.LCDSTAT)
	AND	#0x02
	JR	NZ,1$
	LD	(HL),E
	POP	HL
	POP	DE
	POP	BC
	RET

	; Move the cursor left
.l_curs:
	LD	A,(.curx)	; x coordinate
	CP	#0
	RET	Z
	DEC	A
	LD	(.curx),A
	RET

	; Move the cursor right
.r_curs:
	LD	A,(.curx)	; x coordinate
	CP	#.MAXCURSPOSX
	RET	Z
	INC	A
	LD	(.curx),A
	RET

	; Move the cursor up
.u_curs:
	LD	A,(.cury)	; y coordinate
	CP	#0
	RET	Z
	DEC	A
	LD	(.cury),A
	RET

	; Move the cursor down
.d_curs:
	LD	A,(.cury)	; y coordinate
	CP	#.MAXCURSPOSY
	RET	Z
	INC	A
	LD	(.cury),A
	RET

	; Advance the cursor
.adv_curs::
	PUSH	HL
	LD	HL,#.curx	; x coordinate
	LD	A,#.MAXCURSPOSX
	CP	(HL)
	JR	Z,1$
	INC	(HL)
	JR	99$
1$:
	LD	(HL),#0x00
	LD	HL,#.cury	; y coordinate
	LD	A,#.MAXCURSPOSY
	CP	(HL)
	JR	Z,2$
	INC	(HL)
	JR	99$
2$:
	CALL	.scroll
99$:
	POP	HL
	RET

	; Rewind the cursor
.rew_curs::
	PUSH	HL
	LD	HL,#.curx	; x coordinate
	XOR	A		; A = 0
	CP	(HL)
	JR	Z,1$
	DEC	(HL)
	JR	99$
1$:
	LD	(HL),#.MAXCURSPOSX
	LD	HL,#.cury	; y coordinate
	XOR	A		; A = 0
	CP	(HL)
	JR	Z,99$
	DEC	(HL)
99$:
	POP	HL
	RET

	; Advance the cursor to the next line
.cr_curs::
	PUSH	HL
	XOR	A		; A = 0
	LD	(.curx),A
	LD	HL,#.cury	; y coordinate
	LD	A,#.MAXCURSPOSY
	CP	(HL)
	JR	Z,2$
	INC	(HL)
	JR	99$
2$:
	CALL	.scroll
99$:
	POP	HL
	RET

	; Scroll the whole screen
.scroll:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	HL,#0x9800
	LD	BC,#0x9800+0x20	; BC = next line
	LD	E,#0x20-0x01	; E = height - 1
1$:
	LD	D,#0x20		; D = width
2$:
	LDH	A,(.LCDSTAT)
	AND	#0x02
	JR	NZ,2$

	LD	A,(BC)
	LD	(HL+),A
	INC	BC
	DEC	D
	JR	NZ,2$
	DEC	E
	JR	NZ,1$

	LD	D,#0x20
3$:
	LDH	A,(.LCDSTAT)
	AND	#0x02
	JR	NZ,3$

	LD	A,#.SPACE
	LD	(HL+),A
	DEC	D
	JR	NZ,3$
	POP	HL
	POP	DE
	POP	BC
	RET

	; Clear the whole screen
.cls:
	PUSH	DE
	PUSH	HL
	LD	HL,#0x9800
	LD	E,#0x20		; E = height
1$:
	LD	D,#0x20		; D = width
2$:
	LDH	A,(.LCDSTAT)
	AND	#0x02
	JR	NZ,2$

	LD	(HL),#.SPACE
	INC	HL
	DEC	D
	JR	NZ,2$
	DEC	E
	JR	NZ,1$
	POP	HL
	POP	DE
	RET

_gets::
	LD	A,(.mode)
	CP	#.T_MODE
	JR	Z,1$
	PUSH	BC
	PUSH	DE
	CALL	.tmode
	POP	DE
	POP	BC
1$:
	LDA	HL,2(SP)
	LD	A,(HL+)		; first_tile
	LD	H,(HL)
	LD	L,A
	PUSH	HL
	CALL	.get_string
	POP	HL
	RET

_getchar::
	LD	A,(.mode)
	CP	#.T_MODE
	JR	Z,1$
	PUSH	BC
	PUSH	DE
	CALL	.tmode
	POP	DE
	POP	BC
1$:
	CALL	.get_char
	LD	H,#0x00
	LD	L,A
	RET

_putchar::
	LD	A,(.mode)
	CP	#.T_MODE
	JR	Z,1$
	PUSH	BC
	PUSH	DE
	CALL	.tmode
	POP	DE
	POP	BC
1$:
	LDA	HL,2(SP)
	LD	A,(HL)
	CALL	.put_char
	RET

_gotoxy::
	LD	A,(.mode)
	CP	#.T_MODE
	JR	Z,1$
	PUSH	BC
	PUSH	DE
	CALL	.tmode
	POP	DE
	POP	BC
1$:
	LDA	HL,2(SP)
	LD	A,(HL+)		; x
	LD	(.curx),A
	INC	HL
	LD	A,(HL+)		; y
	LD	(.cury),A
	RET

_posx::
	LD	A,(.mode)
	CP	#.T_MODE
	JR	Z,1$
	PUSH	BC
	PUSH	DE
	CALL	.tmode
	POP	DE
	POP	BC
1$:
	LD	H,#0x00
	LD	A,(.curx)
	LD	L,A
	RET

_posy::
	LD	A,(.mode)
	CP	#.T_MODE
	JR	Z,1$
	PUSH	BC
	PUSH	DE
	CALL	.tmode
	POP	DE
	POP	BC
1$:
	LD	H,#0x00
	LD	A,(.cury)
	LD	L,A
	RET

_setchar::
	LD	A,(.mode)
	CP	#.T_MODE
	JR	Z,1$
	PUSH	BC
	PUSH	DE
	CALL	.tmode
	POP	DE
	POP	BC
1$:
	LDA	HL,2(SP)
	LD	A,(HL)
	CALL	.set_char
	RET

	.area	_DATA

.tp1:

.pointers:

	; Tile 0x00
	.byte	0xFF,0xFF,0xFE,0x82,0xFC,0x84,0xFC,0x84,0xFE,0x82,0xFF,0xB1,0xCF,0xC9,0x87,0x87

.endtp1:

.tp2:
	.include	"lib/font_a.h"
.endtp2:

.tp3:
	.include	"lib/font_b.h"
.endtp3:

.frame_tiles:
	.byte	0x1C,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x1D
	.byte	0x0F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0F
	.byte	0x0F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0F
	.byte	0x0F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0F
	.byte	0x0F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0F
	.byte	0x0F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0F
	.byte	0x0F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0F
	.byte	0x0F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0F
	.byte	0x0F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0F
	.byte	0x1E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x1F

.kbdtable:
	.ascii	| !"#$%&'()*+,-./|
	.ascii	"0123456789:;<=>?"
	.ascii	"@ABCDEFGHIJKLMNO"
	.ascii	"PQRSTUVWXYZ[\\]^_"
	.ascii	"`abcdefghijklmno"
	.ascii	"pqrstuvwxyz{|}~ "
