; font.ms
;
;	Michael Hope, 1999
;	michaelh@earthling.net
;	Distrubuted under the Artistic License - see www.opensource.org
;
	.include        "global.s"
	INCLUDE		"wait_hbl.mh"

	.globl	.cr_curs
	.globl	.adv_curs
	.globl	.cury, .curx
	.globl	.display_off

	; Structure offsets
	sfont_handle_sizeof	=	3
	sfont_handle_font	=	1
	sfont_handle_first_tile	=	0

	; Encoding types - lower 2 bits of font
	FONT_256ENCODING	=	0
	FONT_128ENCODING	=	1
	FONT_NOENCODING		=	2

	; Other bits
	FONT_BCOMPRESSED	=	2
	
	.CR     		= 	0x0A          ; Unix
	.SPACE			=	0x00

	; Maximum number of fonts
	.MAX_FONTS		= 	6

	.area	_HEADER (ABS)

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

	.module font.ms

	; Globals from drawing.s
	; FIXME: Hmmm... check the linkage of these
	.globl	.fg_colour
	.globl	.bg_colour

	.area	_BSS
	; The current font
font_current::
	.ds	sfont_handle_sizeof
	; Cached copy of the first free tile
font_first_free_tile::
	.ds	1
	; Table containing descriptors for all of the fonts
font_table::
	.ds	sfont_handle_sizeof*.MAX_FONTS
	
	.area   _CODE

	; Copy uncompressed 16 byte tiles from (BC) to (HL), length = DE*2
	; Note: HL must be aligned on a UWORD boundry
font_copy_uncompressed::
	ld	a,d
	or	e
	ret	z

	ld	a,h
	cp	#0x98
	jr	c,4$
	sub	#0x98-0x88
	ld	h,a
4$:
	xor	a
	cp	e		; Special for when e=0 you will get another loop
	jr	nz,1$
	dec	d
1$:
	WAIT_HBL
	ld	a,(bc)
	ld	(hl+),a
	inc	bc
	
	WAIT_HBL
	ld	a,(bc)
	ld	(hl),a
	inc	bc

	inc	l
	jr	nz,2$
	inc	h
	ld	a,h		; Special wrap-around
	cp	#0x98
	jr	nz,2$
	ld	h,#0x88
2$:
	dec	e
	jr	nz,1$
	dec	d
	bit	7,d		; -1?
	jr	z,1$
	ret

	; Copy a set of compressed (8 bytes/cell) tiles to VRAM
	; Sets the foreground and background colours based on the current
	; font colours
	; Entry:
	;	From (BC) to (HL), length (DE) where DE = #cells * 8
	;	Uses the current fg_colour and bg_colour fields
font_copy_compressed::
	ld	a,d
	or	e
	ret	z		; Do nothing

	ld	a,h
	cp	#0x98		; Take care of the 97FF -> 8800 wrap around
	jr	c,font_copy_compressed_loop
	sub	#0x98-0x88
	ld	h,a
font_copy_compressed_loop:
	push	de
	ld	a,(bc)
	ld	e,a
	inc	bc
	push	bc

	ld	bc,#0
				; Do the background colour first
	ld	a,(.bg_colour)
	bit	0,a
	jr	z,font_copy_compressed_bg_grey1
	ld	b,#0xFF
font_copy_compressed_bg_grey1:
	bit	1,a
	jr	z,font_copy_compressed_bg_grey2
	ld	c,#0xFF
font_copy_compressed_bg_grey2:
	; BC contains the background colour
	; Compute what xoring we need to do to get the correct fg colour
	ld	d,a
	ld	a,(.fg_colour)
	xor	d
	ld	d,a

	bit	0,d
	jr	z,font_copy_compressed_grey1
	ld	a,e
	xor	b
	ld	b,a
font_copy_compressed_grey1:
	bit	1,d
	jr	z,font_copy_compressed_grey2
	ld	a,e
	xor	c
	ld	c,a
font_copy_compressed_grey2:
	WAIT_HBL
	ld	(hl),b
	inc	hl
	WAIT_HBL
	ld	(hl),c
	inc	hl
	ld	a,h		; Take care of the 97FFF -> 8800 wrap around
	cp	#0x98
	jr	nz,1$
	ld	h,#0x88
1$:
	pop	bc
	pop	de
	dec	de
	ld	a,d
	or	e
	jr	nz,font_copy_compressed_loop
	ret
	
; Load the font HL
font_load::
	call	.display_off
	push	hl

	; Find the first free font entry
	ld	hl,#font_table+sfont_handle_font
	ld	b,#.MAX_FONTS
font_load_find_slot:
	ld	a,(hl)		; Check to see if this entry is free
	inc	hl		; Free is 0000 for the font pointer
	or	(hl)
	cp	#0
	jr	z,font_load_found

	inc	hl
	inc	hl
	dec	b
	jr	nz,font_load_find_slot
	pop	hl
	ld	hl,#0
	jr	font_load_exit	; Couldn't find a free space
font_load_found:
				; HL points to the end of the free font table entry
	pop	de
	ld	(hl),d		; Copy across the font struct pointer
	dec	hl
	ld	(hl),e

	ld	a,(font_first_free_tile)
	dec	hl
	ld	(hl),a		

	push	hl
	call	font_set	; Set this new font to be the default
	
	; Only copy the tiles in if were in text mode
	ld	a,(.mode)
	and	#.T_MODE
	
	call	nz,font_copy_current

				; Increase the 'first free tile' counter
	ld	hl,#font_current+sfont_handle_font
	ld	a,(hl+)
	ld	h,(hl)
	ld	l,a

	inc	hl		; Number of tiles used
	ld	a,(font_first_free_tile)
	add	a,(hl)
	ld	(font_first_free_tile),a

	pop	hl		; Return font setup in HL
font_load_exit:
	;; Turn the screen on
	LDH     A,(.LCDC)
	OR      #0b10000001     ; LCD           = On
				; BG            = On
	AND     #0b11100111     ; BG Chr        = 0x8800
				; BG Bank       = 0x9800
	LDH     (.LCDC),A

	RET

	; Copy the tiles from the current font into VRAM
font_copy_current::	
				; Find the current font data
	ld	hl,#font_current+sfont_handle_font
	ld	a,(hl+)
	ld	h,(hl)
	ld	l,a

	inc	hl		; Points to the 'tiles required' entry
	ld	e,(hl)
	ld	d,#0
	rl	e		; Multiple DE by 8
	rl	d
	rl	e
	rl	d
	rl	e
	rl	d		; DE has the length of the tile data
	dec	hl

	ld	a,(hl)		; Get the flags
	push	af		
	and	#3			; Only lower 2 bits set encoding table size

	ld	bc,#128
	cp	#FONT_128ENCODING	; 0 for 256 char encoding table, 1 for 128 char
	jr	z,font_copy_current_copy

	ld	bc,#0
	cp	#FONT_NOENCODING
	jr	z,font_copy_current_copy

	ld	bc,#256			; Must be 256 element encoding
font_copy_current_copy:
	inc	hl
	inc	hl		; Points to the start of the encoding table
	add	hl,bc		
	ld	c,l
	ld	b,h		; BC points to the start of the tile data		

	; Find the offset in VRAM for this font
	ld	a,(font_current+sfont_handle_first_tile)	; First tile used for this font
	ld	l,a		
	ld	h,#0
	add	hl,hl
	add	hl,hl
	add	hl,hl
	add	hl,hl

	ld	a,#0x90		; Tile 0 is at 9000h
	add	a,h
	ld	h,a
				; Is this font compressed?
	pop	af		; Recover flags
	bit	FONT_BCOMPRESSED,a
				; Do the jump in a mildly different way
	jp	z,font_copy_uncompressed
	jp	font_copy_compressed

	; Set the current font to HL
font_set::
	ld	a,(hl+)
	ld	(font_current),a
	ld	a,(hl+)
	ld	(font_current+1),a
	ld	a,(hl+)
	ld	(font_current+2),a
	ret
	
	;; Print a character with interpretation
.put_char::
	; See if it's a special char
	cp	#.CR
	jr	nz,1$

	; Now see if were checking special chars
	push	af
	ld	a,(.mode)
	and	#.M_NO_INTERP
	jr	nz,2$
	call	.cr_curs
	pop	af
	ret
2$:
	pop	af
1$:
	CALL    .set_char
	CALL    .adv_curs
	RET

	;; Print a character without interpretation
.out_char::
	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	af
	ld	a,(font_current+2)
	; Must be non-zero if the font system is setup (cant have a font in page zero)
	or	a
	jr	nz,3$

	; Font system is not yet setup - init it and copy in the ibm font
	; Kind of a compatibility mode
	call	_font_init
	
	; Need all of the tiles
	xor	a
	ld	(font_first_free_tile),a

	.globl	_font_ibm_fixed
	ld	hl,#_font_ibm_fixed
	call	font_load
3$:
	pop	af
	push	bc
	push	de
	push	hl
				; Compute which tile maps to this character
	ld	e,a
	ld	hl,#font_current+sfont_handle_font
	ld	a,(hl+)
	ld	h,(hl)
	ld	l,a
	ld	a,(hl+)
	and	#3
	cp	#FONT_NOENCODING
	jr	z,set_char_no_encoding
	inc	hl
				; Now at the base of the encoding table
				; E is set above
	ld	d,#0
	add	hl,de
	ld	e,(hl)		; That's the tile!
set_char_no_encoding:
	ld	a,(font_current+0)
	add	a,e
	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

	WAIT_HBL
	LD      (HL),E
	POP     HL
	POP     DE
	POP     BC
	RET

_putchar::
	LDA     HL,2(SP)        ; Skip return address
	LD      A,(HL)          ; A = c
	CALL    .put_char
	RET

_setchar::
	push	bc
	LDA     HL,4(SP)        ; Skip return address
	LD      A,(HL)          ; A = c
	CALL    .set_char
	pop	bc
	RET

_font_load::
	push	bc
	LDA     HL,4(SP)        ; Skip return address and bc
	LD      A,(HL)          ; A = c
	inc	hl
	ld	h,(hl)
	ld	l,a
	call    font_load
	push	hl
	pop	de		; Return in DE
	pop	bc
	RET

_font_set::
	push	bc
	LDA     HL,4(SP)        ; Skip return address
	LD      A,(HL)          ; A = c
	inc	hl
	ld	h,(hl)
	ld	l,a
	call	font_set
	pop	bc
	ld	de,#0		; Always good...
	RET

_font_init::
	push	bc
	.globl	.tmode

	call	.tmode

	ld	a,#1		; We use the first tile as a space _always_
	ld	(font_first_free_tile),a

	; Clear the font table
	xor	a
	ld	hl,#font_table
	ld	b,#sfont_handle_sizeof*.MAX_FONTS
1$:
	ld	(hl+),a
	dec	b
	jr	nz,1$
	ld	a,#3
	ld	(.fg_colour),a
	ld	a,#0
	ld	(.bg_colour),a

	call	.cls
	pop	bc
	ret
	
_print_repeat::
	push	bc
	lda	hl,4(sp)
	ld	e,(hl)
	inc	hl
	ld	d,(hl)
	xor	a
	cp	d
	jr	nz,_print_repeat_go
	pop	bc
	ret
_print_repeat_go:
	ld	a,e
	push	de
	call	.put_char
	pop	de
	dec	d
	jr	nz,_print_repeat_go
	pop	bc
	ret

_gotoxy::
	lda	hl,2(sp)
	ld	a,(hl+)
	ld	(.curx),a
	ld	a,(hl)
	ld	(.cury),a
	ret

_cls::
.cls::	
	PUSH	DE
	PUSH	HL
	LD	HL,#0x9800
	LD	E,#0x20		; E = height
1$:
	LD	D,#0x20		; D = width
2$:
	WAIT_HBL

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

	; Support routines
_posx::
	LD	A,(.mode)
	AND	#.T_MODE
	JR	NZ,1$
	PUSH	BC
	CALL	.tmode
	POP	BC
1$:
	LD	A,(.curx)
	LD	E,A
	RET

_posy::
	LD	A,(.mode)
	AND	#.T_MODE
	JR	NZ,1$
	PUSH	BC
	CALL	.tmode
	POP	BC
1$:
	LD	A,(.cury)
	LD	E,A
	RET

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

.cr_curs::
	PUSH	HL
	XOR	A
	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

.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$:
	;; See if scrolling is disabled
	LD	A,(.mode)
	AND	#.M_NO_SCROLL
	JR	Z,3$
	;; Nope - reset the cursor to (0,0)
	XOR	A
	LD	(.cury),A
	LD	(.curx),A
	JR	99$
3$:	
	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,(.STAT)
	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,(.STAT)
	AND	#0x02
	JR	NZ,3$

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

	.area	_BSS
.curx::				; Cursor position
	.ds	0x01
.cury::
	.ds	0x01
	.area	_CODE

	.globl	.vbl
	.globl	.lcd
	.globl	.int_0x40
	.globl	.int_0x48
	.globl	.remove_int

	;; Enter text mode
.tmode::
	DI			; Disable interrupts

	;; Turn the screen off
	LDH	A,(.LCDC)
	BIT	7,A
	JR	Z,1$

	;; Turn the screen off
	CALL	.display_off

	;; Remove any interrupts setup by the drawing routine
	LD	BC,#.vbl
	LD	HL,#.int_0x40
	CALL	.remove_int
	LD	BC,#.lcd
	LD	HL,#.int_0x48
	CALL	.remove_int
1$:

	CALL	.tmode_out

	;; Turn the screen on
	LDH	A,(.LCDC)
	OR	#0b10000001	; LCD		= On
				; BG		= On
	AND	#0b11100111	; BG Chr	= 0x8800
				; BG Bank	= 0x9800
	LDH	(.LCDC),A

	EI			; Enable interrupts

	RET

	;; Text mode (out only)
.tmode_out::
	XOR	A
	LD	(.curx),A
	LD	(.cury),A

	;; Clear screen
	CALL	.cls

	LD	A,#.T_MODE
	LD	(.mode),A

	RET
