;================================================================
; Description
;================================================================

; CharAde Character Printing Engine
; ---------------------------------
;
; by Jason "Joefish" Railton
; (C) Copyright January 2020
;
; version: B34 dated 19 Jan 2020
;
;
;
; This program, called from BASIC, diverts the LPRINT command
; to this engine, which can print user-defined characters,
; blocks and maps.
;
; It can print to a back-screen to hold level data, copy that
; to a foreground screen, add sprites, then copy that to the
; visible screen. All work is done with character-mapped data,
; which is then rendered to the main screen using one of four
; pre-defined 64-character fonts.
;
; This source code is copyright, but the compiled code and any
; graphics data designed into it become free to use in any
; manner by the designer of those graphics, who may assert
; any rights over those graphics themselves.
;
; A copy of this source code may be provided for information
; only. Ammendments and corrections are welcome to be
; submitted to the author via the Spectrum Computing forums.
;

;================================================================
; Control Codes
;================================================================

;Control Codes (as ASCII):

;	..  ...
;	32	SPACE
;	-------
; Codes 33..47 ! " # $ % & ' ( ) * + , - . /
;	33	"!"		Clear all screens
;	34	""""	Set cursor Y/X on screen
;	35	"#"		Load character set N
;	36	"$"		Draw block N, move cursor 2 characters to the right
;	37	"%"		Set cursor Y/X behind foreground characters <= N  [%NYX]
;	38	"&"		Set cursor Y/X on background
;	39	"'"		Clear foreground
;	40	"("		Copy background to foreground
;	41	")"		Render foreground to screen
;	42	"*"		Copy background character to foreground at cursor
;	43	"+"		Scan Y/X area for highest character
;	44	","		Newline to character below last cursor placement
;	45	"-"		Scan Y/X area for lowest non-zero character
;	46	"."		Skip one character place
;	47	"/"		Newline upward
;	-------
;	48	"0"
;	..  ...
;	57	"9"
;	-------
; Codes 58..64 : ; < = > ? @
;	58	":"		Offset background relative to foreground. Y/X moves UP/LEFT
;	59	";"		Set block sub offset Y/X and paint foreground from map
;	60	"<"		Parse Keyboard QAOPM/SPACE
;	61	"?"		Read character at cursor
;	62	">"		Parse Kempston joystick
;	63	"="		Redefine characters N..(*C) as set S character M..
;	64	"@"		Set cursor Y/X on foreground
;	-------
;	65	"A"
;	..  ...
;	90	"Z"
;	-------
; Code 94 ^ (up-arrow)
;	94	"^"		Subtract background offset from foreground cursor position
;	-------
;	95	"_"
;	97	"a"
;	..  ...
;  122	"z"


;New UDG Character Codes:
;
;	00..10 = [SPACE]1234567890
;	11..36 = ABCDEFGHIJKLMNOPQRSTUVWXYZ
;	37..62 = abcdefghijklmnopqrstuvwxyz
;	    63 = _


;================================================================
; Definitions
;================================================================

;start point:
	ORG1				equ 54100

;Control codes:
	FIRST_CTRL_CODE		equ 32
	
;Macros:
	MACRO if_a_zero, addr
		and a
		jp z, addr
	ENDM
	
	MACRO if_a_eq, n, addr
		cp n
		jp z, addr
	ENDM

	MACRO if_a_gt, n, addr
		cp n+1
		jp nc, addr
	ENDM

	MACRO if_a_lt, n, addr
		cp n
		jp c, addr
	ENDM

	MACRO if_a_gteq, n, addr
		cp n
		jp nc, addr
	ENDM

	MACRO if_a_lteq, n, addr
		cp n+1
		jp c, addr
	ENDM

;System Variables:
	CHANS		equ 23631

;================================================================
; Start:
;================================================================
;Origin point in RAM:
	org ORG1
	
;Skippable points so options can be encoded in start address,
;and padding to get variables to a memorable spot:
entry_point
		REPT 3
			nop
		ENDM

;Start here:
startup_entry
		;Calculate the input configuration options:
		ld hl,-ORG1		; official entry point is ORG1
		add hl,bc		; BC holds actual entry point, so now HL=difference
		
		rr l			; Code 1 = 16-line rendering, not 18
		jp nc,eighteen_line_rendering
	sixteen_line_rendering
		ld a,16
		ld (render_make_count+1),a
		ld bc,render_done
		ld (last_render_row_done-64-64),bc
		jp line_rendering_ready
	eighteen_line_rendering
		ld a,18
		ld (render_make_count+1),a
		ld bc,last_render_row-64
		ld (last_render_row_done-64-64),bc
	line_rendering_ready
		
		;Pre-load the first font into the renderer:
		xor a
		call fn_load_set_a
		
		;Hijack the LPRINT Channel, #3:
		ld hl,(CHANS)
		ld bc,5*3
		add hl,bc
		
		ld bc,my_print
		ld de,my_input
		ld (hl),c
		inc hl
		ld (hl),b
		inc hl
		ld (hl),e
		inc hl
		ld (hl),d
		
		;Reset variables:
		ld hl,response_area
		ld de,response_area+1
		ld bc,var_end-response_area-1
		ld (hl),0
		ldir
		
		ld hl,background
		ld (var_background),hl
		ld hl,foreground
		ld (var_foreground),hl
		ld hl,block_data
		ld (var_blocks),hl
		
		;Return pointer to response data area:
		ld bc,response_area
		
		;That's it for now:
		ret
	        
	
;================================================================
; Protected area:
;================================================================

; Stack pointer storage:
render_sp	defw	0


;================================================================
; Response area:
;================================================================

response_area

get_char_result			defb	0

control_scan_up			defb	0
control_scan_down		defb	0
control_scan_left		defb	0
control_scan_right		defb	0
control_scan_fire		defb	0
control_scan_fire_tap	defb	0

var_blocks				defw	0
var_foreground			defw	0
var_background			defw	0


;================================================================
; Variables
;================================================================
var_start

cur_screen	defb	0	; 0=foreground, 1=under fg, 2=bg, 3=screen
cur_yx
cur_x		defb	0
cur_y		defb	0
cur_ox		defb	0

cutoff_char	defb	0

bkgd_dyx
bkgd_dx		defb	0
bkgd_dy		defb	0

bkgd_syx
bkgd_sx		defb	0
bkgd_sy		defb	0

font		defb	0

scan_w		defb	0
scan_h		defb	0

redef_tgt	defb	0
redef_num	defb	0
redef_font	defb	0
redef_char	defb	0

scan_fire_kempston	defb	0
scan_fire_keyboard	defb	0

var_end


;================================================================
; New LPRINT routine
;================================================================
;Codes 0-31 are special functions
;Codes 32, 33 (SPACE, '!') have special behaviour
;Codes 34-127 PRINT characters
my_print
print_divert equ $+1
		jp mode_normal
	
	mode_normal
		if_a_lt FIRST_CTRL_CODE, print_done
		if_a_gt 127, print_done
		if_a_eq 94, high_ctrl_code
		if_a_gt 64, print_char
		if_a_gt 57, mid_ctrl_code
		if_a_lt 48, low_ctrl_code

		
;Print a Character:
;------------------
print_char
		call sub_get_char
	do_print_char
		ex af,af'	;for later
	
		ld a,(cur_screen)
		if_a_zero print_char_foreground
		if_a_eq 1, print_char_under_foreground
		if_a_eq 2, print_char_background
	print_char_screen
		ld a,(cur_y)
		if_a_gt 21, print_done

		;Calculate attribute address,
		;don't touch char code in c,
		;a holds (cur_y) already:
		ld d,a
		ld e,0
		srl d	
		rr  e
		srl d
		rr  e
		srl d	
		rr  e
		ld a,22528/256
		or d
		ld d,a
		ld a,(cur_x)
		or e
		ld e,a
		push de
		
		;Calculate character address:
		;(char code in c)
		ld a,(font)
		rrca
		rrca
		ld c,a
		ex af,af'
		or c
		ld b,0
		ld c,a
		ld hl,font_data
		add hl,bc
		sla c
		rl  b
		sla c
		rl  b
		sla c
		rl  b		
		add hl,bc
		
		;Calculate screen address:
		ld a,(cur_y)
		ld d,a
		and 7
		rrca
		rrca
		rrca
		ld e,a
		ld a,(cur_x)
		or e
		ld e,a
		ld a,d
		and 24
		or 16384/256
		ld d,a
		
		REPT 7
			ld a,(hl)
			ld (de),a
			inc hl
			inc d
		ENDM
		ld a,(hl)
		ld (de),a
		inc hl

		pop de
		ld a,(hl)
		ld (de),a
		jp printed_char_done
		
	print_char_under_foreground
		call calc_foreground_addr
		
		ld a,(cutoff_char)			; cutoff value for transparent character
		ld b,(hl)
		rlc b
		rlc b
		rlc b
		cp b
		jp c,printed_char_done		; IF char(hl) > cutoff_char THEN don't print
		jp put_char_foreground

	print_char_foreground
		call calc_foreground_addr
		
	put_char_foreground
		;Put character:
		ex af,af'
		rrca
		rrca
		rrca		;Character is pre-rotated for multiplication by 32
		ld (hl),a
		jp printed_char_done

		
	print_char_background
		call calc_background_addr
		
		ex af,af'
		rrca
		rrca
		rrca		;Character is pre-rotated
		ex af,af'
		
		;Put character:
		ex af,af'
		ld (hl),a
		jp printed_char_done

		
	calc_foreground_addr
		;Calculate foreground address in HL:
		ld hl,(cur_yx)
		xor a
		srl h
		rr  a
		srl h
		rr  a
		srl h
		rr  a	; >>3 is <<5 is *32
		or l
		ld l,a
		ld a,foreground/256
		add a,h
		ld h,a
		ret
		
		
	calc_background_addr
		;Calculate background address in HL:
		ld hl,(cur_yx)
		xor a
		srl h
		rr  a
		srl h
		rr  a
		srl h
		rr  a	; >>3 is <<5 is *32
		or l
		ld l,a
		ld a,background/256
		add a,h
		ld h,a
		ret
				
		
;En/Decoding Functions:
;----------------------
sub_get_char
		if_a_lt   48, sub_got_char_space
		if_a_eq   48, sub_got_char_ten
		if_a_lt   58, sub_got_char_digit
		if_a_lt   65, sub_got_char_space
		if_a_lt   91, sub_got_char_caps
		if_a_lt   97, sub_got_char_max
		if_a_lt  123, sub_got_char_lower
sub_got_char_max
		ld a,63
		ret
sub_got_char_lower
		sub 60
		ret
sub_got_char_caps
		sub 54
		ret
sub_got_char_digit
		sub 48
		ret
sub_got_char_ten
		ld a,10
		ret
sub_got_char_space
		xor a
		ret
		
		
sub_get_param
		if_a_lt   32, sub_got_param
		if_a_lt   48, sub_param_zero
		if_a_eq   48, sub_param_ten
		if_a_lt   58, sub_param_digit
		if_a_lt   65, sub_param_zero
		if_a_lt   87, sub_param_caps_a_v
		if_a_lt   97, sub_param_zero
		if_a_lt   119, sub_param_lower_a_v
	sub_param_max
		ld a,31
		ret
	sub_param_lower_a_v
		sub 86
		ret
	sub_param_caps_a_v
		sub 54
		ret
	sub_param_digit
		sub 48
		ret
	sub_param_ten
		ld a,10
		ret
	sub_param_zero
		xor a
	sub_got_param
		ret
	
sub_get_param_font
		call sub_get_param
		if_a_eq   0, sub_param_font_1
		if_a_gt   4, sub_param_font_4
		ret
	sub_param_font_1
		ld a,1
		ret
	sub_param_font_4
		ld a,4
		ret
	

;Tabled Functions:
;-----------------
; ASCII code 94 enter here:
high_ctrl_code		
		sub 29		; lowers ASCII code 94 to 65
		
; ASCII codes 58..64 enter here:
mid_ctrl_code		
		sub 10		; lowers ASCII codes 58..64 to 48..54 and 65 (above) to 55
		
; ASCII codes 33..47 enter here:
low_ctrl_code		
		;Jump to the function in the function table:
		ld b,0
		ld c,a
		sla c
		ld hl,fn_table-(FIRST_CTRL_CODE*2)
		add hl,bc
		ld e,(hl)
		inc hl
		ld d,(hl)
		ex de,hl
		jp (hl)
	
	print_done
		ret	
	
	print_all_done
	next_print_normal
		ld hl,mode_normal
		ld (print_divert),hl
		ret
		

;Character functions:
;--------------------
fn_print_space
		xor a
		jp do_print_char

printed_char_done
fn_skip_char
		ld a,(cur_x)
		inc a
		and 31
		ld (cur_x),a
		ret

		
fn_copy_background_char
		call calc_foreground_addr
		call sub_copy_background_char
		jp fn_skip_char
		
	sub_copy_background_char
		;Check on the right screen and mode:
		ld a,(cur_screen)
		if_a_zero do_copy_background_char
		if_a_eq 1, test_copy_background_under_char
		ret
		
	test_copy_background_under_char
		ld a,(cutoff_char)			; cutoff value for transparent character
		ld b,(hl)
		rlc b
		rlc b
		rlc b
		cp b
		ret c						; IF char(hl) > cutoff_char THEN don't print
		
	do_copy_background_char
		ex de,hl
		
		;Calculate background address:
		ld bc,(bkgd_dyx)
		ld a,(cur_y)
		add a,b
		and 31
		ld h,a
		ld a,(cur_x)
		add a,c
		and 31
		ld l,a
		xor a
		srl h
		rr  a
		srl h
		rr  a
		srl h
		rr  a	; >>3 is <<5 is *32
		or l
		ld l,a
		ld a,background/256
		add a,h
		ld h,a

		ld a,(hl)
		ld (de),a
		ret

		
;Block functions:
;--------------------
fn_draw_block_n
		ld hl,fn_next_set_draw_block_n
		ld (print_divert),hl
		ret
	fn_next_set_draw_block_n
		call sub_get_char
		
		;Calculate block address:
		ld l,0
		srl a
		rr  l
		srl a
		rr  l
		srl a
		rr  l
		srl a
		rr  l	; >>4 is <<4 is *16
		add a,block_data/256
		ld h,a

		ld d,4
		ld a,(cur_screen)
		if_a_eq 2, print_block_background
		
		
print_block_foreground
		;If printing to foreground, e=1
		ld e,1
		jp do_print_block
		
		
print_block_background
		;If printing to background, e=0
		ld e,0
		;jp do_print_block
				
				
do_print_block
		ld bc,(cur_yx)
		push bc
		
		;Row counter:
		ld d,4
		
	do_print_block_loop
			push bc
			REPT 4		
				ld a,(hl)
				push de
				push hl
				
				;On foreground, e=1, don't print character code 0:
				cp e
				call c,do_print_block_skip		
				call nc,do_print_char
				
				pop hl
				pop de
				inc hl
			ENDM
			pop bc
			ld a,b
			inc a
			and 31
			ld b,a
			ld (cur_yx),bc
	
			dec d
			jp nz,do_print_block_loop

		;move cursor two characters to right:
		pop bc
		ld a,c
		inc a
		inc a
		and 31
		ld c,a
		ld (cur_yx),bc
		
		jp next_print_normal

		
	do_print_block_skip
		ld a,(cur_x)
		inc a
		and 31
		ld (cur_x),a
		scf
		ret
		
		
		
;Screen functions:
;-----------------
fn_clear_all
		di
		ld (render_sp),sp
		ld sp,foreground+2048
		ld de,0
		ld b,64
	clear_loop
		REPT 16
			push de
		ENDM
		djnz clear_loop
		ld sp,(render_sp)
		ei
		ret
		
		
fn_clear_foreground
		di
		ld (render_sp),sp
		ld sp,foreground+1024
		ld de,0
		ld b,32
		jp clear_loop

		
fn_copy_background
		;Start position on background:
		ld bc,(bkgd_dyx)

		;Horizontal wrap point in copy loop:
		ld a,32
		sub c
		rlca
		ld d,0
		ld e,a
		ld hl,copy_background_line
		add hl,de
		push hl
		ld (hl),$01	;ld bc,
		inc hl
		ld (hl),224	;-32
		inc hl
		ld (hl),255	;
		inc hl
		ld (hl),$09	;add hl,bc

		;Number of lines before vertical wrap:
		ld a,32
		sub b
		ex af,af'
		
		;Number of lines to copy:
		ld a,32
		
		;Start address on background:
		push af
		xor a
		srl b
		rr  a
		srl b
		rr  a
		srl b
		rr  a	; >>3 is <<5 is *32
		or c
		ld c,a
		ld hl,background
		add hl,bc
		pop af
				
		;Start address on foreground:
		ld de,foreground
	copy_background_loop
		
		copy_background_line
			REPT 32+2
				ldi
			ENDM

			ld bc,32
			add hl,bc
			
			ex af,af'
			dec a
			jr nz,copy_background_no_wrap
			ld bc,64512		; = -1024
			add hl,bc
		copy_background_no_wrap
			ex af,af'
		
			dec a
			jp nz,copy_background_loop
			
		pop hl		
		ld (hl),$ED	;...
		inc hl
		ld (hl),$A0	;ldi
		inc hl
		ld (hl),$ED	;...
		inc hl
		ld (hl),$A0	;ldi
		
		ret

		
;Cursor functions:
;-----------------
fn_set_y_x_under_foreground_n
		ld a,1
		ld (cur_screen),a
		ld hl,fn_next_set_under_cutoff_n
		ld (print_divert),hl
		ret
	fn_next_set_under_cutoff_n
		call sub_get_char
		ld (cutoff_char),a
		ld hl,fn_next_set_under_cur_y
		ld (print_divert),hl
		ret
	fn_next_set_under_cur_y
		call sub_get_param
		ld (cur_y),a
		ld hl,fn_next_set_under_cur_x
		ld (print_divert),hl
		ret
	fn_next_set_under_cur_x
		call sub_get_param
		ld (cur_x),a
		ld (cur_ox),a
		jp next_print_normal
		
		
fn_set_y_x_foreground
		xor a
		ld (cur_screen),a
		jp fn_set_y_x_cur
	
fn_set_y_x_background
		ld a,2
		ld (cur_screen),a
		jp fn_set_y_x_cur
		
fn_set_y_x_screen
		ld a,3
		ld (cur_screen),a
		;jp fn_set_y_x_cur
		
fn_set_y_x_cur
		ld hl,fn_next_set_cur_y
		ld (print_divert),hl
		ret
	fn_next_set_cur_y
		call sub_get_param
		ld (cur_y),a
		ld hl,fn_next_set_cur_x
		ld (print_divert),hl
		ret
	fn_next_set_cur_x
		call sub_get_param
		ld (cur_x),a
		ld (cur_ox),a
		jp next_print_normal
	
		
fn_set_background_offset_y_x
		ld hl,fn_next_set_background_offset_y
		ld (print_divert),hl
		ret
	fn_next_set_background_offset_y
		call sub_get_param
		ld (bkgd_dy),a
		ld hl,fn_next_set_background_offset_x
		ld (print_divert),hl
		ret
	fn_next_set_background_offset_x
		call sub_get_param
		ld (bkgd_dx),a
		jp next_print_normal

		
fn_sub_background_offset
		ld bc,(bkgd_dyx)
        ld a,(cur_screen)
        cp 2
        jp z,offset_back_front
        ret nc
        
offset_front_back
		ld a,(cur_x)
		add a,c
		ld c,a
		ld a,(cur_y)
		add a,b
		ld b,a
		ld a,2
		jp offset_apply

offset_back_front
		ld a,(cur_x)
		sub c
		ld c,a
		ld a,(cur_y)
		sub b
		ld b,a
		xor a
				
offset_apply
        ld (cur_screen),a
		ld a,c
		and 31
		ld (cur_x),a
		ld (cur_ox),a
		ld a,b
		and 31
		ld (cur_y),a
		ret
				
				
fn_block_map_at_offset_sy_sx
		ld hl,fn_next_set_map_offset_sy
		ld (print_divert),hl
		ret
	fn_next_set_map_offset_sy
		call sub_get_param
		and 3
		ld (bkgd_sy),a
		ld hl,fn_next_set_map_offset_sx
		ld (print_divert),hl
		ret
	fn_next_set_map_offset_sx
		call sub_get_param
		and 3
		ld (bkgd_sx),a

		;Get dy to map background to top-left of foreground, and correct for top-left of viewport:
		ld a,(bkgd_dy)
		inc a
		and 31
		ld d,a
		
		ld a,(bkgd_dx)
		inc a
		ld b,a	;copy A for testing
		and 31
		ld e,a
		
		;If map wraps-around right-to-left, offset vertically by half:
		ld a,b
		and 32
		jp z,map_init_x_ok
	map_init_offset_y	
		ld a,d
		xor 15
		ld d,a
	map_init_x_ok		
		
		;Get y to top-left of viewport, then correct by sy:
		ld a,(bkgd_sy)
		neg
		add a,4
		ld b,a
		
		ld a,(bkgd_sx)
		neg
		add a,4 
		ld c,a
		
		;Block map row loop:
		ld a,6
	block_map_row_loop
		push af
		push bc
		push de
		
		;Block map column loop:
		ld a,7
	block_map_column_loop
		push af
		push bc
		push de

		;Calculate background map address:
		ld h,d
		xor a
		srl h
		rr  a
		srl h
		rr  a
		srl h
		rr  a	; >>3 is <<5 is *32
		or e
		ld l,a
		ld a,background/256
		add a,h
		ld h,a
		;Get block:
		ld a,(hl)
						
		;Calculate block address in DE:
		rrca	;already shifted 3 times when printed. Now 4, which is x16
		ld d,a
		and 240
		ld e,a
		ld a,d
		and 15
		add a,block_data/256
		ld d,a		

		;Calculate foreground address in HL:
		ld h,b
		xor a
		srl h
		rr  a
		srl h
		rr  a
		srl h
		rr  a	; >>3 is <<5 is *32
		or c
		ld l,a
		ld a,foreground/256
		add a,h
		ld h,a
		
		;Paint block:
		ld a,4
	block_paint_row_loop
		push af
		
		REPT 4,rept_num
			ld a,(de)
			rrca
			rrca
			rrca
			ld (hl),a
			inc e
			inc l
		ENDM
		
		;Next block paint row:
		ld bc,32-4
		add hl,bc
		
		pop af
		dec a
		jp nz,block_paint_row_loop
				
	;Next block map column:
		pop de
		ld a,e
		inc a
		ld b,a	;copy A for testing
		and 31
		ld e,a

		;If map wraps-around right-to-left, offset vertically by half:
		ld a,b
		and 32
		jp z,map_loop_x_ok
	map_loop_offset_y
		ld a,d
		xor 15
		ld d,a
	map_loop_x_ok		
				
		pop bc
		inc c
		inc c
		inc c
		inc c

		pop af
		dec a
		jp nz,block_map_column_loop
		
	;Next block map row:
		pop de
		ld a,d
		inc a
		and 31
		ld d,a

		pop bc
		inc b
		inc b
		inc b
		inc b
				
		pop af
		dec a
		jp nz,block_map_row_loop
		
		jp next_print_normal
		
		
fn_newline
		ld a,(cur_ox)
		ld (cur_x),a
		ld a,(cur_y)
		inc a
		and 31
		ld (cur_y),a
		ret
		
		
fn_newline_upward
		ld a,(cur_ox)
		ld (cur_x),a
		ld a,(cur_y)
		dec a
		and 31
		ld (cur_y),a
		ret

		
;Scan functions:
;---------------
fn_get_char	
		;Calculate address:
		ld bc,(cur_yx)
		xor a
		srl b
		rr  a
		srl b
		rr  a
		srl b
		rr  a	; >>3 is <<5 is *32
		or c
		ld c,a
		
		ld a,(cur_screen)		
		if_a_lt 2, get_char_foreground
		if_a_eq 2, get_char_background
		ret
		
	get_char_foreground
		ld hl,foreground
		add hl,bc
		ld a,(hl)
		rlca
		rlca
		rlca		;Because character was pre-rotated when printed
		ld (get_char_result),a
		ret
		
	get_char_background
		ld hl,background
		add hl,bc
		ld a,(hl)
		ld (get_char_result),a
		ret
		
		
fn_scan_y_x_max
		ld hl,fn_next_scan_max_y
		ld (print_divert),hl
		ret
	fn_next_scan_max_y
		call sub_get_param
		ld (scan_h),a
		ld hl,fn_next_scan_max_x
		ld (print_divert),hl
		ret
	fn_next_scan_max_x
		call sub_get_param
		ld (scan_w),a
		;Modify code with scan handler:
		ld hl,handle_scan_max
		ld (call_scan_handler+1),hl
		jp fn_scan_y_x
		
		
fn_scan_y_x_min
		ld hl,fn_next_scan_min_y
		ld (print_divert),hl
		ret
	fn_next_scan_min_y
		call sub_get_param
		ld (scan_h),a
		ld hl,fn_next_scan_min_x
		ld (print_divert),hl
		ret
	fn_next_scan_min_x
		call sub_get_param
		ld (scan_w),a
		;Modify code with scan handler:
		ld hl,handle_scan_min
		ld (call_scan_handler+1),hl
		;jp fn_scan_y_x

		
fn_scan_y_x
		;Initial zero result:
		xor a
		ld (get_char_result),a
		
		;Modify code with screen address:
		ld hl,foreground
		ld a,(cur_screen)		
		if_a_lt 2, scan_foreground
		ld hl,background
	scan_foreground
		ld (scan_screen_addr+1),hl
		
		;Vertical scan loop:
		ld a,(cur_y)
		ld b,a
		ld a,(scan_h)
	scan_vert_loop
		push af
		
		;Horizontal scan loop:
		ld a,(cur_x)
		ld c,a
		ld a,(scan_w)
	scan_horz_loop
		push af
		
		;Calculate address, preserving co-ordinates in BC:
		ld d,b
		xor a
		srl d
		rr  a
		srl d
		rr  a
		srl d
		rr  a	; >>3 is <<5 is *32
		or c
		ld e,a
		;The modifiable bit of the calculation:
	scan_screen_addr
		ld hl,foreground
		add hl,de
		
		;Get character:
		ld a,(hl)
		rlca
		rlca
		rlca		;Because character was pre-rotated when printed
		
		;Pass to appropriate max/min handler (modifiable):
	call_scan_handler
		call handle_scan_min

		;And loop:
		ld a,c
		inc a
		and 31
		ld c,a
		
		pop af
		dec a
		jp nz,scan_horz_loop
		
		ld a,b
		inc a
		and 31
		ld b,a
		
		pop af
		dec a
		jp nz,scan_vert_loop
		
		;And finish:
		jp next_print_normal
		
	handle_scan_min
		and a
		ret z						;return if A = 0
		ld d,a
		ld a,(get_char_result)
		and a
		jp z,scan_min_override		;if best character so far is zero, always use new one
		cp d
		ret c						;return if D > A, new char > old char
	scan_min_override
		ld a,d
		ld (get_char_result),a		;D <= A, new char <= old char
		ret
		
	handle_scan_max
		and a
		ret z						;return if A = 0
		ld d,a
		ld a,(get_char_result)
		cp d
		ret nc						;return if D <= A, new char <= old char
		ld a,d
		ld (get_char_result),a		;D > A, new char > old char
		ret
		
		
;Font functions:
;---------------
fn_load_set_n
		ld hl,fn_next_load_set_n
		ld (print_divert),hl
		ret
	fn_next_load_set_n
		call sub_get_param_font
		dec a
		ld (font),a
		
		call fn_load_set_a
		jp next_print_normal
		
	fn_load_set_a
		rlc a
		ld b,a
		rrc a
		rrc a
		rrc a
		ld c,a
		ld hl,font_data+8
		add hl,bc

		;Set colour for SPACE:		
		ld a,(hl)
		ld (render_chars+2),a
		inc hl
		
		;Set remaining characters:		
		ld de,render_chars+32+1
		ld a,63
	load_set__loop
		REPT 8
			ldi
			inc de
			inc de
		ENDM
		inc de
		ldi
		inc de
		inc de
		inc de
		inc de
		inc de
		inc de
		
		dec a
		jp nz,load_set__loop
		
		ret

		
fn_redefine_n_c_as_s_m
		ld hl,fn_next_redefine_n
		ld (print_divert),hl
		ret
	fn_next_redefine_n
		call sub_get_char
		ld (redef_tgt),a
		ld hl,fn_next_redefine_c
		ld (print_divert),hl
		ret
	fn_next_redefine_c
		call sub_get_param
		ld (redef_num),a
		ld hl,fn_next_redefine_s
		ld (print_divert),hl
		ret
	fn_next_redefine_s
		call sub_get_param_font
		ld (redef_font),a
		ld hl,fn_next_redefine_m
		ld (print_divert),hl
		ret
	fn_next_redefine_m
		call sub_get_char
		ld (redef_char),a

		ld a,(redef_num)
		and a
		jp z,fn_redefine_done
		
		;Calculate target char address in DE:
		ld a,(redef_tgt)
		and a
		jp z,fn_redefine_done
		ld e,0
		srl a
		rr  e
		srl a
		rr  e
		srl a
		rr  e	; >>3 is <<5 is *32
		add a,render_chars/256
		ld d,a 
		
		;Calculate source char address in HL:
		ld a,(redef_font)
		dec a
		rrca
		rrca
		ld c,a
		ld a,(redef_char)
		or c
		ld c,a
		ld b,0
		ld hl,font_data
		add hl,bc
		sla c
		rl  b
		sla c
		rl  b
		sla c
		rl  b		
		add hl,bc

		; B = redef_tgt, A = redef_char
		ld a,(redef_tgt)
		ld b,a
		ld a,(redef_char)
		;IF redef_tgt >= redef_char THEN B = redef_tgt
		cp b
		jp c,fn_redef_tgt_gt_redef_char		;Jump if B>A
		ld b,a	;IF A>=B THEN B=A
	fn_redef_tgt_gt_redef_char
		; Now B = MAX (redef_tgt, redef_char)
		;Check the number of characters requested fit:
		ld a,64
		sub b
		ld b,a
		;Now B = number of valid characters left that can be copied
		ld a,(redef_num)
		cp b
		jp c,fn_redef_length_ok		;Jump if B>A
		ld a,b	;IF A<=B THEN A=B
	fn_redef_length_ok
		
	fn_redef_loop
		REPT 8
			inc de
			ldi
			inc de
		ENDM
		inc de
		inc de
		ldi
		inc de
		inc de
		inc de
		inc de
		inc de
		
		dec a
		jp nz,fn_redef_loop
				
	fn_redefine_done
		jp next_print_normal

		
;Render function:
;----------------
fn_render
		halt
		di
		ld (render_sp),sp
		
	render_make_map
		ld de,foreground+(32*4)+4+23
		ld sp,render_list+11+(24*2)
		
	render_make_count
		ld a,18
		render_make_map_row_loop
			ex af,af'
			;First one:
				ld a,(de)
				and 7
				add a,render_chars/256
				ld b,a
				ld a,(de)
				and 224
				ld c,a
				push bc
			;Remaining 23 out of 24:
			REPT 23
				dec de		
				ld a,(de)
				and 7
				add a,render_chars/256
				ld b,a
				ld a,(de)
				and 224
				ld c,a
				push bc
			ENDM

			ld hl,32+24-1
			add hl,de
			ex de,hl
			
			ld hl,64+48
			add hl,sp
			ld sp,hl
						
			ex af,af'
			dec a
			jp nz,render_make_map_row_loop
		
	render_call
		jp render_list
		
	render_done
		ld sp,(render_sp)
		ei
		ret
	
		
;Control input functions:
;------------------------
fn_parse_keyboard
		xor a
		ld (control_scan_fire_tap),a
		
		ld bc,$FBFE
		in a,(c)
		cpl
		and 1
		ld (control_scan_up),a
		
		ld b,$FD
		in a,(c)
		cpl
		and 1
		ld (control_scan_down),a
		
		ld b,$DF
		in a,(c)
		cpl
		and 3
		rra
		ld (control_scan_left),a
		rla
		and 1
		ld (control_scan_right),a
		
		ld b,$7F
		in a,(c)
		cpl
		and 13	;SPACE + N + M
		jp nz,parse_keyboard_fire
		
	parse_keyboard_no_fire
		xor a
		ld (control_scan_fire),a
		ld (scan_fire_keyboard),a
		ret
		
	parse_keyboard_fire
		ld a,1
		ld (control_scan_fire),a
		ex af,af'
		ld a,(scan_fire_keyboard)
		ex af,af'
		ld (scan_fire_keyboard),a
		ex af,af'
		and a
		ret nz
		ex af,af'
		ld (control_scan_fire_tap),a
		ret

		
fn_parse_kempston
		xor a
		ld (control_scan_fire_tap),a
		
		ld c,31
		in b,(c)
		
		xor a
		rr b
		rl a
		ld (control_scan_right),a
		
		xor a
		rr b
		rl a
		ld (control_scan_left),a
		
		xor a
		rr b
		rl a
		ld (control_scan_down),a
		
		xor a
		rr b
		rl a
		ld (control_scan_up),a
		
		xor a
		rr b
		rl a
		ld (control_scan_fire),a
		and a
		jp nz,parse_kempston_fire
		
	parse_kempston_no_fire
		ld (scan_fire_kempston),a
		ret
		
	parse_kempston_fire
		ex af,af'
		ld a,(scan_fire_kempston)
		ex af,af'
		ld (scan_fire_kempston),a
		ex af,af'
		and a
		ret nz
		ex af,af'
		ld (control_scan_fire_tap),a
		ret

	
;================================================================
; Tables
;================================================================

;Print Function Codes Table:
;---------------------------

fn_table	;codes 32..47, 58..64, 94
		defw	fn_print_space					; [SPACE]
		
		; Codes 33..47 ! " # $ % & ' ( ) * + , - . /
		defw	fn_clear_all					; !
		defw	fn_set_y_x_screen				; "
		defw	fn_load_set_n					; #
		defw	fn_draw_block_n					; $
		defw	fn_set_y_x_under_foreground_n	; %
		defw	fn_set_y_x_background			; &
		defw	fn_clear_foreground				; '
		defw	fn_copy_background				; (
		defw	fn_render						; )
		defw	fn_copy_background_char			; *
		defw	fn_scan_y_x_max					; +
		defw	fn_newline						; ,
		defw	fn_scan_y_x_min					; -
		defw	fn_skip_char					; .
		defw	fn_newline_upward				; /
		
		; Codes 48..54 (was 58..64) : ; < = > ? @
		defw	fn_set_background_offset_y_x	; :
		defw	fn_block_map_at_offset_sy_sx	; ;
		defw	fn_parse_keyboard				; <
		defw	fn_redefine_n_c_as_s_m			; =
		defw	fn_parse_kempston				; >
		defw	fn_get_char						; ?
		defw	fn_set_y_x_foreground			; @
	
		; Code 55 (was 94) ^
		defw	fn_sub_background_offset		; ^ (up-arrow)
	
		
;================================================================
; New INPUT #3 routine
;================================================================
my_input
		ret

		
;================================================================
; Data Space
;================================================================

;256-byte Page Alignment:
IF ($ AND 255)
	defs	256-($ AND 255),255
ENDIF

;Character renderer function list:
; 64 chars x 32 b = 2048 b = 2 Kb = 8 pg
; 2 Kb = 8 pg
render_chars
render_space
		inc l			;  1b
		ld a,0			;  3b
		ld (de),a		;  4b
		inc e			;  5b
		ret				;  6b
		REPT 26			; 32b
			nop
		ENDM
		
		REPT 63
			REPT 7
				ld (hl),0		;  2b
				inc h			;  3b
			ENDM			; 21b
			ld (hl),0		; 23b
			ld h,b			; 24b
			inc l			; 25b
			ld a,0			; 27b
			ld (de),a		; 28b
			inc e			; 29b
			ret				; 30b
			nop				; 31b
			nop				; 32b
		ENDM

		
;16x16 Block Data:	
; 64 blocks x 16 b = 1024 b = 1 Kb = 4 pg
; 3 Kb = 12 pg
block_data
		REPT 64, block_id
			defb block_id,0,0,block_id, 0,0,0,0, 0,0,0,0, block_id,0,0,block_id
		ENDM

		
;Foreground Character Map:
; 32 x 32 = 1024 b = 1 Kb = 4pg
; 4 Kb = 16 pg
foreground				;must come first, then background.
		defs 1024, 0
		
		
;Background Character Map:
; 32 x 32 = 1024 b = 1 Kb = 4pg
; 5 Kb = 20 pg
background
		defs 1024, 0

		
;Screen Renderer List:		
; 18 rows x 64 b = 1152 b = 1.125 Kb = 4.5 pg
; 6.125 Kb = 24.5 pg
render_list
		REPT 6,render_steps_top_third
			ld hl,16384 + 4 + 64 + (render_steps_top_third * 32)	;  3b
			ld b,h													;  4b
			ld de,22528 + 4 + 64 + (render_steps_top_third * 32)	;  7b
			ld sp,$+6												; 10b
			jp render_space											; 13b codes at +11
			REPT 23											;46b
				defw render_space						; 2b
			ENDM													; 59b
			defw $+5												; 61b
			nop														; 62b
			nop														; 63b
			nop														; 64b per row
		ENDM
		
		REPT 8, render_steps_middle_third
			ld hl,16384 + 4 + 2048 + (render_steps_middle_third * 32)
			ld b,h
			ld de,22528 + 4 +  256 + (render_steps_middle_third * 32)
			ld sp,$+6												; 10b
			jp render_space											; 13b
			REPT 23											;46b
				defw render_space						; 2b
			ENDM													; 59b
			defw $+5												; 61b
			nop														; 62b
			nop														; 63b
			nop														; 64b
		ENDM
		
		REPT 3, render_steps_bottom_third
			ld hl,16384 + 4 + 4096 + (render_steps_bottom_third * 32)
			ld b,h
			ld de,22528 + 4 +  512 + (render_steps_bottom_third * 32)
			ld sp,$+6												; 10b
			jp render_space											; 13b
			REPT 23											;46b
				defw render_space						; 2b
			ENDM													; 59b
			defw $+5												; 61b
			nop														; 62b
			nop														; 63b
			nop														; 64b
		ENDM
	last_render_row
		ld hl,16384 + 4 + 4096 + (3 * 32)						;  3b
		ld b,h													;  4b
		ld de,22528 + 4 +  512 + (3 * 32)						;  7b
		ld sp,$+6												; 10b
		jp render_space											; 13b
		REPT 23											;46b
			defw render_space						; 2b
		ENDM													; 59b
	last_render_row_done
		defw render_done										; 61b

		
;Font Data
; 256 x 9 b = 2304 b = 2.25 Kb = 9 pg
; 8.375 Kb = 33.5 pg
font_data
	REPT 4,font_id
		defb 0, 0, 0, 0, 0, 0, 0, 0				, 0		;   Space #0
		
		defb 0, 24, 40, 8, 8, 8, 62, 0			, 65	; 1	#1
		defb 0, 60, 66, 2, 60, 64, 126, 0		, 66	; 2
		defb 0, 60, 66, 12, 2, 66, 60, 0		, 67	; 3
		defb 0, 8, 24, 40, 72, 126, 8, 0		, 68	; 4
		defb 0, 126, 64, 124, 2, 66, 60, 0		, 69	; 5
		defb 0, 60, 64, 124, 66, 66, 60, 0		, 70	; 6
		defb 0, 126, 2, 4, 8, 16, 16, 0			, 71	; 7
		defb 0, 60, 66, 60, 66, 66, 60, 0		,  4	; 8
		defb 0, 60, 66, 66, 62, 2, 60, 0		,  5	; 9 Nine #9
		defb 0, 60, 70, 74, 82, 98, 60, 0		,  7	; 0 Zero #10
		
		defb 0, 60, 66, 66, 126, 66, 66, 0		,113	; A Upper Case #11
		defb 0, 124, 66, 124, 66, 66, 124, 0	,113	; B
		defb 0, 60, 66, 64, 64, 66, 60, 0		,113	; C
		defb 0, 120, 68, 66, 66, 68, 120, 0		,113	; D
		defb 0, 126, 64, 124, 64, 64, 126, 0	,113	; E
		defb 0, 126, 64, 124, 64, 64, 64, 0		,113	; F
		defb 0, 60, 66, 64, 78, 66, 60, 0		,113	; G
		defb 0, 66, 66, 126, 66, 66, 66, 0		,113	; H
		defb 0, 62, 8, 8, 8, 8, 62, 0			,113	; I
		defb 0, 2, 2, 2, 66, 66, 60, 0			,113	; J
		defb 0, 68, 72, 112, 72, 68, 66, 0		,113	; K
		defb 0, 64, 64, 64, 64, 64, 126, 0		,113	; L
		defb 0, 66, 102, 90, 66, 66, 66, 0		,113	; M
		defb 0, 66, 98, 82, 74, 70, 66, 0		,113	; N
		defb 0, 60, 66, 66, 66, 66, 60, 0		,113	; O
		defb 0, 124, 66, 66, 124, 64, 64, 0		,113	; P
		defb 0, 60, 66, 66, 82, 74, 60, 0		,113	; Q
		defb 0, 124, 66, 66, 124, 68, 66, 0		,113	; R
		defb 0, 60, 64, 60, 2, 66, 60, 0		,113	; S
		defb 0, 254, 16, 16, 16, 16, 16, 0		,113	; T
		defb 0, 66, 66, 66, 66, 66, 60, 0		,113	; U
		defb 0, 66, 66, 66, 66, 36, 24, 0		,113	; V
		defb 0, 66, 66, 66, 66, 90, 36, 0		,113	; W
		defb 0, 66, 36, 24, 24, 36, 66, 0		,113	; X
		defb 0, 130, 68, 40, 16, 16, 16, 0		,113	; Y
		defb 0, 126, 4, 8, 16, 32, 126, 0		,113	; Z #36
		
		defb 0, 0, 56, 4, 60, 68, 60, 0			, 57	; a Lower Case #37
		defb 0, 32, 32, 60, 34, 34, 60, 0		, 57	; b
		defb 0, 0, 28, 32, 32, 32, 28, 0		, 57	; c
		defb 0, 4, 4, 60, 68, 68, 60, 0			, 57	; d
		defb 0, 0, 56, 68, 120, 64, 60, 0		, 57	; e
		defb 0, 12, 16, 24, 16, 16, 16, 0		, 57	; f
		defb 0, 0, 60, 68, 68, 60, 4, 56		, 57	; g
		defb 0, 64, 64, 120, 68, 68, 68, 0		, 57	; h
		defb 0, 16, 0, 48, 16, 16, 56, 0		, 57	; i
		defb 0, 4, 0, 4, 4, 4, 36, 24			, 57	; j
		defb 0, 32, 40, 48, 48, 40, 36, 0		, 57	; k
		defb 0, 16, 16, 16, 16, 16, 12, 0		, 57	; l
		defb 0, 0, 104, 84, 84, 84, 84, 0		, 57	; m
		defb 0, 0, 120, 68, 68, 68, 68, 0		, 57	; n
		defb 0, 0, 56, 68, 68, 68, 56, 0		, 57	; o
		defb 0, 0, 120, 68, 68, 120, 64, 64		, 57	; p
		defb 0, 0, 60, 68, 68, 60, 4, 6			, 57	; q
		defb 0, 0, 28, 32, 32, 32, 32, 0		, 57	; r
		defb 0, 0, 56, 64, 56, 4, 120, 0		, 57	; s
		defb 0, 16, 56, 16, 16, 16, 12, 0		, 57	; t
		defb 0, 0, 68, 68, 68, 68, 56, 0		, 57	; u
		defb 0, 0, 68, 68, 40, 40, 16, 0		, 57	; v
		defb 0, 0, 68, 84, 84, 84, 40, 0		, 57	; w
		defb 0, 0, 68, 40, 16, 40, 68, 0		, 57	; x
		defb 0, 0, 68, 68, 68, 60, 4, 56		, 57	; y
		defb 0, 0, 124, 8, 16, 32, 124, 0		, 57	; z #62
		
		IF (font_id=0)
			defb 11000110b, 10111010b, 01111100b, 01010100b, 11101110b, 11111110b, 01111100b, 01101100b, 70	; _ #63	
		ENDIF
		IF (font_id=1)
			defb 01111111b, 00001000b, 10011100b, 11110010b, 11111010b, 00011110b, 00010101b, 00111110b, 69	; _ #63	
		ENDIF
		IF (font_id=2)
			defb 00000110b, 00000110b, 00000010b, 00001010b, 01010110b, 11100100b, 11101011b, 01101001b, 68	; _ #63	
		ENDIF
		IF (font_id=3)
			defb 10000111b, 01111010b, 10101100b, 10101101b, 01110100b, 10001101b, 11111111b, 01111110b, 66	; _ #63	
		ENDIF
	ENDM
	
	
;================================================================
;End of Program
;================================================================
;Set entry point
	end entry_point
;================================================================
