03-23-2014, 04:09 PM
First Third pass, if you'd like to do testing:
(you'll also need an FXZ font; incbin at @FONT in asm context)
(you'll also need an FXZ font; incbin at @FONT in asm context)
Code:
SUB FZXSetFont (FontAddress as uInteger)
POKE UINTEGER @FZXFontPointer+1,FontAddress
RETURN
END SUB
SUB FZXPrintAt(Y as uByte, X as uByte, printData as String)
DIM FZXi as uByte=0
FZXPrintAChar(22) 'at
FZXPrintAChar(Y)
FZXPrintAChar(X)
IF LEN printData > 0 THEN
FOR FZXi=0 to LEN printData-1
FZXPrintAChar(CODE printData(FZXi))
NEXT FZXi
END IF
END SUB
SUB FZXPrint(printData as String)
DIM FZXi as uInteger
IF LEN printData > 0 THEN
FOR FZXi=0 to LEN printData-1
FZXPrintAChar(CODE printData(FZXi))
NEXT FZXi
END IF
END SUB
SUB FASTCALL FZXPrintAChar (character as uByte)
ASM
PUSH IX
CALL FZX_START
POP IX
RET
; -----------------------------------------------------------------------------
; FZX driver - Copyright (c) 2013 Einar Saukas
; -----------------------------------------------------------------------------
; org 65000 ; driver address
;FONT EQU 60000 ; font address
MARGIN EQU 0 ; left margin (in pixels)
;STR_NUM EQU 4 ; stream #4
; -----------------------------------------------------------------------------
; CREATE CHANNEL AND ATTACH STREAM
DRIVER:
; ld hl, ($5c53) ; store system variable PROG in HL
; dec hl
; ld bc, 5 ; allocate 5 bytes for channel below BASIC area
; push bc
; call $1655 ; call routine MAKE-ROOM
; pop bc
; ld hl, CH_DATA + 4
; lddr ; copy CH_DATA to new channel space
; ld hl, ($5c4f) ; store system variable CHANS in HL
; ex de, hl
; inc hl
; inc hl ; now HL = allocated address + 1
; sbc hl, de ; calculate offset between start of channels
; ; area and start of the new channel space
; ; (notice the carry flag was already cleared
; ; from executing CALL $1655 earlier)
; ld (STR_OFF), hl ; attach stream by storing channel address
; ; offset in streams table
; ret
;STR_OFF EQU $5c10+((STR_NUM+3)*2) ; address of channel offset in streams table
;CH_DATA:
; defw START ; address of the PRINT # routine
; defw $15c4 ; address of the INPUT # routine
; defb 'S' ; channel type 'S'
; -----------------------------------------------------------------------------
; PROPORTIONAL PRINT ROUTINE
FZX_START:
ld hl, FZX_P_FLAG ; initial address of local variables
dec (hl) ; check P_FLAG value by decrementing it
jp m, CHK_AT ; expecting a regular character?
jr z, GET_COL ; expecting the AT column?
GET_LIN:
cpl
add a, 192 ; now A = 191 - char
inc hl
GET_COL:
inc hl
ld (hl), a
ret
CHK_AT:
cp 22 ; specified keyword 'AT'?
jr nz, CHK_CR
ld (hl), 2 ; change P_FLAG to expect line value next time
RET
CHK_CR:
inc (hl) ; increment P_FLAG to restore previous value
inc hl
END ASM
FZXFontPointer:
ASM
ld bc, FONT
push bc
pop ix
cp 13
jp z, NEWLINE
CHK_CHAR:
dec a ; now A = char - 1
cp (ix+2) ; compare with lastchar
jr nc, UNDEF_CHAR
sub 31 ; now A = char - 32
jr nc, PRINT_CHAR
UNDEF_CHAR:
ld a, '?'-32 ; print '?' instead of invalid character
PRINT_CHAR:
inc a ; now A = char - 31
ld l, a
ld h, 0
ld d, h
ld e, l
add hl, hl
add hl, de ; now HL = (char - 31) * 3
add hl, bc ; now HL references offset/kern in char table
ld e, (hl)
inc hl
ld a, (hl)
and 63
ld d, a ; now DE = offset
xor (hl)
rlca
rlca
ld c, a ; now C = kern
push hl
add hl, de
dec hl ; now HL = char definition address
ex (sp), hl ; now HL references offset/kern in char table
inc hl ; now HL references shift/width in char table
xor a
rld ; now A = char shift
push af
rld ; now A = (width - 1)
ld (WIDTH1+1), a
cp 8 ; check if char width is larger than 8 bits
rld ; restore char shift/width
ld de, $000e ; same as "LD C,0"
jr c, NARROW_CHAR
ld de, $234e ; same as "LD C,(HL)" and "INC HL"
NARROW_CHAR:
ld (SMC), de ; self-modify code to handle narrow/large chars
inc hl ; now HL references next char offset
ld a, (hl) ; now A = LSB of next char offset
add a, l
ld e, a ; now E = LSB of next char definition address
ld hl, FZX_P_COL
ld a, (hl)
sub c ; move left number of pixels specified by kern
jr nc, ON_SCREEN ; stop moving if it would fall outside screen
xor a
ON_SCREEN:
ld (hl), a
ld a, (WIDTH1+1) ; now A = (width - 1)
add a, (hl) ; now A = (width - 1) + column
call c, NEWLINE ; if char width won't fit then move to new line
ld bc, (FZX_P_COL)
ld a, 1
sub (ix+0) ; now A = 1 - height
add a, b ; now A = P_LIN - height + 1
jp nc, $0c86 ; call routine REPORT-5 ("Out of screen")
pop af ; now A = shift
add a, 191 ; range 0-191
call $22aa + 2 ; call PIXEL-ADD + 2 to calculate screen address
ex af, af'
; now A' = (col % 8)
jr CHK_LOOP
MAIN_LOOP:
ld d, (hl) ; now D = 1st byte from char definition grid
inc hl ; next character definition
SMC:
ld c, (hl) ; now C = 2nd byte from char definition or zero
inc hl ; (either "LD C,0" or "LD C,(HL)" + "INC HL")
xor a ; now A = zero (since there's no 3rd byte)
ex (sp), hl ; now HL = screen address
ex af, af'
; now A = (col % 8), A' = 0
jr z, NO_ROTATE
ld b, a ; now B = (col % 8)
ex af, af'
; now A = 0, A' = (col % 8)
ROTATE_PIXELS:
srl d ; rotate right char definition grid in D,C,A
rr c
rra
djnz ROTATE_PIXELS
NO_ROTATE:
inc l
inc l
or (hl)
ld (hl), a ; put A on screen
dec l
ld a, c
or (hl)
ld (hl), a ; put C on screen
dec l
ld a, d
or (hl)
ld (hl), a ; put D on screen
inc h ; move screen address by 1 pixel down
ld a, h
and 7
jr nz, CHK_LOOP
ld a, l
add a, 32
ld l, a
jr c, CHK_LOOP
ld a, h
sub 8
ld h, a
CHK_LOOP:
ex (sp), hl ; now HL = char definition address
ld a, l
cp e ; check if reached next char definition address
jr nz, MAIN_LOOP ; loop otherwise
pop hl ; discard screen address from stack
ld hl, FZX_P_COL
ld a, (hl) ; now A = column
WIDTH1:
add a, 0 ; now A = column + (width - 1)
scf
adc a, (ix+1) ; now A = column + width + tracking
jr nc, EXIT ; if didn't fall outside the screen then exit
NEWLINE:
ld (hl), MARGIN ; move to initial column at left margin
inc hl
ld a, (hl) ; now A = line
sub (ix+0) ; now A = line - height
EXIT:
ld (hl), a ; move down a few pixels specified by height
RET
FZX_P_FLAG:
defb 0
FZX_P_COL:
defb MARGIN
FZX_P_LIN:
defb 191
FZX_END:
; -----------------------------------------------------------------------------
END ASM
END SUB