;; This code shows how to patch the firmware so that you can use your own char drawing functions with it.
;; The font is a 6 pixel wide font and only works in mode 1.
;; 
;; We patch:
;; - TXT SET PEN - so we can define pen colour of our font using "PEN" from BASIC
;; - TXT SET PAPER - so we can define background colour of our font using "PAPER" from BASIC
;; - TXT WRITE CHAR - so we can draw chars on the screen using "PRINT" and also they are drawn using the built in "line editor"
;;
;; We use TXT GET PEN, TXT GET PAPER to get current pen and paper so we can setup our colours for our drawing code.
;; 
;; Bugs:
;; - Cursor position is wrong.
;; - Cursor colour is wrong.
;; - BASIC insists on a 40 char wide screen. Not sure how to fix yet.
;; 
;; Limitations:
;; - Our font doesn't have lower case chars, we look for those and convert to upper case
;;
;; The pixel data is defined in mode 2 (2 colours, 1 bit per pixel)
;; This is converted at runtime to mode 1 format (4 colours, 2 bits per pixel)
;; And then drawn to the screen
;;
;; The code does some screen pixel manipulate to draw and mask the chars onto the screen.
;;
;; 
org &4000
nolist

;; nibbles swapped!
pen0 equ %00000000
pen1 equ %00001000
pen2 equ %10000000
pen3 equ %10001000

;; aaaa aabb bbbb cccc ccdd dddd eeee eeff ffff
;;  0    1    2    3     4    5   6    7   8

;; 0->0
;; 1->1
;; 2->3
;; 3->4
;; 4->6
;; 5->7
;; 6->9

;;txt_output equ &bb5a
;;txt_set_cursor equ &bb75
txt_set_pen equ &bb90
txt_set_paper equ &bb96
txt_write_char equ &bdd3
scr_char_position equ &bc1a
scr_next_line equ &bc26
scr_char_limits equ &bc17
txt_get_pen equ &bb93
txt_get_paper equ &bb99
txt_draw_cursor equ &bdcd
txt_undraw_cursor equ &bdd0
;;txt_ask_state

start:
;;ld a,&c3
;;ld (txt_output),a
;;ld hl,display_char
;;ld (txt_output+1),hl

ld a,&c3
ld hl,display_cursor
ld (txt_draw_cursor),a
ld (txt_draw_cursor+1),hl
ld (txt_undraw_cursor),a
ld (txt_undraw_cursor+1),hl


ld hl,scr_char_position
ld de,old_scr_char_position
ld bc,3
ldir

ld a,&c3
ld (scr_char_position),a
ld hl,our_scr_char_position
ld (scr_char_position+1),a

ld a,&c3
ld (scr_char_limits),a
ld hl,our_scr_char_limits
ld (scr_char_limits+1),hl

;;ld hl,txt_set_cursor
;;ld de,old_txt_set_cursor
;;ld bc,3
;;ldir

;;ld a,&c3
;;ld (txt_set_cursor),a
;;ld hl,set_txt_coord
;;ld (txt_set_cursor+1),hl

ld hl,txt_set_pen
ld de,old_txt_set_pen
ld bc,3
ldir

ld a,&c3
ld (txt_set_pen),a
ld hl,set_txt_pen
ld (txt_set_pen+1),hl

ld hl,txt_set_paper
ld de,old_txt_set_paper
ld bc,3
ldir

ld a,&c3
ld (txt_set_paper),a
ld hl,set_txt_paper
ld (txt_set_paper+1),hl

ld a,&c3
ld (txt_write_char),a
ld hl,display_char_wr
ld (txt_write_char+1),hl

call txt_get_pen
call txt_set_pen

call txt_get_paper
call txt_set_paper

ret

our_scr_char_limits:
ld b,53			;; 53 chars accross
ld c,25			;; 25 chars down
ret

old_scr_char_position:
defs 3

working_coords:
defw 0

our_scr_char_position:
ld (working_coords),hl
ld h,0
call old_scr_char_position

ld a,(working_coords+1)
srl a
ld c,a
add a,a	;; x2
add a,c ;; x3
ld c,a
ld a,(working_coords+1)
and &1
add a,c
add a,l
ld l,a
ld a,h
adc a,0
ld h,a
ret


;; A = char
;; H = x 
;; L = y
display_char_wr:
ld (txt_coords),hl
jp display_char

;;ld h,0
;;ld l,1
;;call set_txt_coord

;;ld h,pen0
;;ld l,pen1
;;call set_txt_colours

ld hl,message
call display_message
l1:
jp l1

display_message:
ld a,(hl)
inc hl
or a
ret z
call display_char
jr display_message

message: defb "THIS IS A TEST",0	



;; each char is 6 mode 1 pixels wide and 8 mode 1 pixels tall
;; in mode 1, each byte uses 4 pixels
;; so this means that each char uses 2 bytes, with the last 2 pixels of each line are unused.
;; this is done for simplicity.
;;
;; this means each char is 2 bytes wide and 8 bytes tall


;; to work out screen address to draw char to
;; 
;; byte 0  byte 2   byte 3    byte 4        byte 5
;; 0 1 2 3 4 5 6 7  8 9 10 11 12 13 14 15   16 17 18 19

;; first char uses pixels 0,1,2,3,4,5
;; second char uses pixels 6,7,8,9,10,11
;; third char uses 12,13,14,15, 16, 17

;; even/odd x coord

display_char:
push hl
push de
push bc

;; work out location of pixel data
;; 1 bit per pixel font used here
cp 'a'
jr c,display_char3
cp 'z'+1
jr nc,display_char3
and &df
display_char3:
sub ' '
ld l,a
ld h,0
add hl,hl
add hl,hl
add hl,hl
ld de,font
add hl,de
;; convert from 1 bit per pixel to 2 bit per pixel
call depack_char

;; x coord, remove bit 0, and multiply by 3
;; 0->0
;; 2->3
;; 4->6

ld h,0
ld a,(txt_y)
ld l,a
call old_scr_char_position

ld a,(txt_x)
srl a
ld c,a
add a,a	;; x2
add a,c ;; x3
ld c,a
ld a,(txt_x)
and &1
add a,c
add a,l
ld l,a
ld a,h
adc a,0
ld h,a

;; now display char in appropiate location
ld de,char_depack_buffer
ex de,hl

call display_char2

;; increment text coord position
ld hl,txt_x
inc (hl)
pop bc
pop de
pop hl
ret


display_cursor:
call txt_ask_state
and &3
ret nz

push hl
push de
push bc

;; x coord, remove bit 0, and multiply by 3
;; 0->0
;; 2->3
;; 4->6

ld h,0
ld a,(txt_y)
ld l,a
call old_scr_char_position

ld a,(txt_x)
srl a
ld c,a
add a,a	;; x2
add a,c ;; x3
ld c,a
ld a,(txt_x)
and &1
add a,c
add a,l
ld l,a
ld a,h
adc a,0
ld h,a
ex de,hl
call display_cursor2

pop bc
pop de
pop hl
ret

display_char2:
ld a,(txt_x)
and 1
jp nz,display_char_odd
jp display_char_even


display_cursor2:
ld a,(txt_x)
and 1
jp nz,display_cursor_odd
jp display_cursor_even

;; H = x coord
;; L = y coord
set_txt_coord:
ld (txt_coords),hl
old_txt_set_cursor:
defs 3

lookup_pen:
add a,pen_table AND 255
ld l,a
ld a,pen_table/256
adc a,0
ld h,a
ld a,(hl)
ret


pen_table:
defb pen0
defb pen1
defb pen2
defb pen3

set_txt_pen:
push af
call lookup_pen
ld (fg_color+1),a
pop af
old_txt_set_pen:
defs 3

set_txt_paper:
push af
call lookup_pen
ld (bk_color+1),a
pop af
old_txt_set_paper:
defs 3

;; convert 1-bit per pixel into 2 bit per pixel
depack_char:
ld b,8
ld de,char_depack_buffer
depack_char2:
push bc
ld c,(hl)
call depack_byte
call depack_byte
inc hl
pop bc
djnz depack_char2
ret

;; take 4x 1 bit per pixel from one byte and write out 4 2-bit per pixels to one byte

depack_byte:
xor a
call depack_pixel
call depack_pixel
call depack_pixel
call depack_pixel
ld (de),a
inc de
ret

depack_pixel:
call depack_pix
or b
rlca
ret

depack_pix:
;; shift into carry
rlc c
bk_color:
ld b,pen0
ret nc
fg_color:
ld b,pen1
ret


txt_coords:
txt_y:
defb 0
txt_x:
defb 0

;; in this example B is the odd char
;; on screen:
;;
;; aaaa aabb bbbb 
;;  0    1    2   
;;
;; pixels from char:
;; bbbb bb

display_char_odd:
ld b,8
dco1:
push bc
push de

;; read 4 pixels from screen
;; keep left 2 pixels
ld a,(de)
and %11001100
ld c,a
;; read font data
;; shift data two pixels to right
ld a,(hl)
rrca
rrca
and %00110011
;; combine with screen
or c
ld (de),a
inc de

ld a,(hl)
rlca
rlca
and %11001100
ld c,a
inc hl
ld a,(hl)
rrca
rrca
and %00110011
or c
ld (de),a
inc hl
pop de
ex de,hl
call scr_next_line
ex de,hl
pop bc
djnz dco1
ret


;; in this example B is the odd char
;; on screen:
;;
;; aaaa aabb bbbb 
;;  0    1    2   
;;
;; pixels from char:
;; bbbb bb

display_cursor_odd:
ld b,8
dcuo1:
push bc

ld a,(de)
xor %00110011
ld (de),a
inc de
ld a,(de)
cpl
ld (de),a
dec de

ex de,hl
call scr_next_line
ex de,hl
pop bc
djnz dcuo1
ret


;; in this example A is the even char
;; on screen:
;;
;; aaaa aabb bbbb 
;;  0    1    2   
;;
;; pixels from char:
;; aaaa aa

display_char_even:
ld b,8
dce1:
push bc
push de

;; read 4 pixels
ld a,(hl)
;; store to screen
ld (de),a
inc de
inc hl

;; read 4 pixels from screen
ld a,(de)
;; isolate the pixels we don't want to change
and %00110011
ld c,a

;; now read 4 pixels from font
ld a,(hl)
;; isolate pixels we want
and %11001100
;; combine with screen data
or c
;; write back to screen
ld (de),a
;;inc de
inc hl
pop de
ex de,hl
call scr_next_line
ex de,hl
pop bc
djnz dce1
ret


display_cursor_even:
ld b,8
dcue1:
push bc

ld a,(de)
cpl
ld (de),a
inc de
ld a,(de)
xor %11001100
ld (de),a
dec de

ex de,hl
call scr_next_line
ex de,hl
pop bc
djnz dcue1
ret

char_depack_buffer: defs 16

font:
defb &00
defb &00,&00,&00,&00,&00,&00,&00,&00,&10,&10,&10,&10,&00,&10,&00,&00 
defb &28,&28,&00,&00,&00,&00,&00,&00,&28,&7c,&28,&28,&7c,&28,&00,&00 
defb &10,&7c,&50,&7c,&14,&7c,&10,&60,&64,&08,&10,&20,&4c,&0c,&00,&10 
defb &28,&28,&30,&54,&48,&34,&00,&10,&10,&20,&00,&00,&00,&00,&00,&08 
defb &10,&20,&20,&20,&10,&08,&00,&20,&10,&08,&08,&08,&10,&20,&00,&00 
defb &00,&28,&10,&7c,&10,&28,&00,&00,&00,&10,&10,&7c,&10,&10,&00,&00 
defb &00,&00,&00,&00,&20,&20,&40,&00,&00,&00,&00,&7c,&00,&00,&00,&00 
defb &00,&00,&00,&00,&30,&30,&00,&00,&00,&04,&08,&10,&20,&40,&00,&00 
defb &38,&4c,&54,&54,&64,&38,&00,&00,&30,&50,&10,&10,&10,&7c,&00,&00 
defb &38,&44,&04,&38,&40,&7c,&00,&00,&38,&44,&18,&04,&44,&38,&00,&00 
defb &08,&18,&28,&48,&7c,&08,&00,&00,&7c,&40,&78,&04,&44,&38,&00,&00 
defb &38,&40,&78,&44,&44,&38,&00,&00,&7c,&04,&08,&10,&20,&20,&00,&00 
defb &38,&44,&38,&44,&44,&38,&00,&00,&38,&44,&44,&3c,&04,&38,&00,&00 
defb &00,&00,&20,&00,&00,&20,&00,&00,&00,&10,&00,&00,&10,&10,&20,&00 
defb &00,&08,&10,&20,&10,&08,&00,&00,&00,&00,&7c,&00,&7c,&00,&00,&00 
defb &00,&20,&10,&08,&10,&20,&00,&00,&38,&44,&08,&10,&00,&10,&00,&70 
defb &00,&70,&48,&48,&48,&48,&00,&00,&38,&44,&44,&7c,&44,&44,&00,&00 
defb &78,&44,&78,&44,&44,&78,&00,&00,&38,&44,&40,&40,&44,&38,&00,&00 
defb &78,&24,&24,&24,&24,&78,&00,&00,&7c,&40,&78,&40,&40,&7c,&00,&00 
defb &7c,&40,&78,&40,&40,&40,&00,&00,&38,&44,&40,&4c,&44,&38,&00,&00 
defb &44,&44,&7c,&44,&44,&44,&00,&00,&38,&10,&10,&10,&10,&38,&00,&00 
defb &04,&04,&04,&44,&44,&38,&00,&00,&48,&50,&60,&50,&48,&44,&00,&00 
defb &40,&40,&40,&40,&40,&7c,&00,&00,&44,&6c,&54,&44,&44,&44,&00,&00 
defb &44,&64,&54,&4c,&44,&44,&00,&00,&38,&44,&44,&44,&44,&38,&00,&00 
defb &78,&44,&44,&78,&40,&40,&00,&00,&38,&44,&44,&54,&48,&34,&00,&00 
defb &78,&44,&44,&78,&48,&44,&00,&00,&38,&40,&38,&04,&44,&38,&00,&00 
defb &7c,&10,&10,&10,&10,&10,&00,&00,&44,&44,&44,&44,&44,&38,&00,&00 
defb &44,&44,&44,&44,&28,&10,&00,&00,&44,&44,&44,&44,&54,&28,&00,&00 
defb &44,&28,&10,&10,&28,&44,&00,&00,&44,&44,&28,&10,&10,&10,&00,&00 
defb &7c,&08,&10,&20,&40,&7c,&00,&00
list
