Proportional pixel-precise printing anywhere on screen - LCD - 06-02-2010
Finally it works now and does not crash in a SUB. I love this routine! It will be included in BorIDE code library.
Code: ' Proportional printing anywhere on screen.
' Original routine by Christoph Odenthal (Odin)
' ZXBC modification by Leszek Chmielewski (LCD)
' Thanks to Boriel and Britlion for help and hints
dim text$
text$="This is a test for ZX Spectrum proportional print!"
dim x as ubyte
dim y as ubyte
dim spacesize as ubyte
dim xgap as ubyte
x=0
y=168
spacesize=2
xgap=0
propprint(x,y,spacesize,xgap,text$)
sub propprint(x as ubyte,y as ubyte,spacesize as ubyte, xgap as ubyte,txt$ as string)
dim b$ as string '
b$=txt$+chr(0)
poke Uinteger @PropPrintTxtadr,PEEK(Uinteger, @b$)+2 'textadr
poke @PropPrintTxtadr+2,x
poke @PropPrintTxtadr+3,y
poke @PropPrintTxtadr+4,spacesize
poke @PropPrintTxtadr+5,xgap
PropPrint:
Asm
;' --------------------------------
;' Free Size N Place Text Print
;' --------------------------------
;' (c) 14.09.2002 C.Odenthal
;'
;' Last Modifications: 15.10.2002
;' Modification for Boriels ZXBC
;' By Leszek Chmielewski 02.06.2010
;' --------------------------------
;' ORG 32000
;' DUMP 45000
;' --------------------------------
;' Entry point
;' --------------------------------
Start:
JR Start2
;' --------------------------------
;' Parameter
;' --------------------------------
end asm
PropPrintTxtadr:
asm
text_addr:
DEFW 0 ;' Addr of text
x_pos:
DEFB 4 ;' Pos of text
y_pos:
DEFB 4
spc_size:
DEFB 3 ;' Width of space char
x_gap:
DEFB 1 ;' Gap between chars
y_gap:
DEFB 1 ;' Gap between lines
end_symb:
DEFB 0 ;' End of line char code
x_start:
DEFB 0 ;' Start of window
y_start:
DEFB 0
x_end:
DEFB 255 ;' End of window
y_end:
DEFB 191
x_zoom:
DEFB 1 ;' Zooming factor
y_zoom:
DEFB 1
;' --------------------------------
;' Main routine
;' --------------------------------
Start2:
LD A,(x_pos) ;' Calc. scr addr.
LD B,A
LD A,(y_pos)
LD C,A
CALL PosToScr
LD (scr_ad),DE ;' Store scr addr.
LD A,L
LD (pix_pos),A
LD A,(end_symb) ;' Get line ending char
LD (poke_here+1),A ;' Poke it into memory below
LD HL,(text_addr)
Main_Loop:
LD A,(HL);' Get char
poke_here:
CP 0 ;' End of text? (poked!)
JP Z,Exit1
INC HL
PUSH HL
CP 32 ;' Replace ctrl chars
JP NC,No_Ctrl
LD A,32
No_Ctrl:
CALL Copy_Chr ;' Copy char-gfx to work-buffer
CALL Measure ;' Measure left rim + width
PUSH BC ;' (Save results)
LD A,C ;' No pixel set in char?
AND A
JP Z,Space_Chr
LD A,(pix_pos);' size+bitpos (=1..15)
ADD A,C
CP 9 ;' > 8 ?
JP NC,Overlap ;' Char overlaps
;' Calculate 8 bit rotation
LD A,(pix_pos) ;' bitpos-l_rim (=-7..7)
SUB B
AND A ;' = 0 ?
JP Z,PrintIt8
JP C,Neg_8 ;' < 0 ?
;' --------------------------------
CP 5 ;' Not in 1..4 ?
JP NC,Left_8
;' --------------------------------
LD B,A
CALL Chr_Rgt8 ;' Rotate right 8 bit
JP PrintIt8
;' --------------------------------
Left_8:
NEG ;' = 8 - A
ADD A,8
LD B,A
CALL Chr_Left8 ;' Rotate left 8 bit
JP PrintIt8
;' --------------------------------
Neg_8:
NEG
CP 5 ;' Not in 1..4 ?
JP NC,Right_8
;' --------------------------------
LD B,A
CALL Chr_Left8 ;' Rotate left 8 bit
JP PrintIt8
;' --------------------------------
Right_8:
NEG ;' = 8 - A
ADD A,8
LD B,A
CALL Chr_Rgt8 ;' Rotate right 8 bit
JP PrintIt8
;' --------------------------------
;' Calculate 16 bit rotation
;' --------------------------------
Overlap:
LD A,(pix_pos);' bitpos-l_rim (=-7..7)
SUB B
AND A ;' = 0 ?
JP Z,PrintIt8
;' --------------------------------
LD B,A
CALL Chr_Rgt16 ;' Rotate right 16 bit
PrintIt16:
LD DE,(scr_ad);' Check for screen end
LD A,E
AND 31
CP 31 ;' (2nd byte outside ?)
JP Z,Outside
CALL Print16 ;' Display char (16 bit)
JP Next_Loc
;' --------------------------------
PrintIt8:
LD DE,(scr_ad);' Display char (8 bit)
CALL Print8
JP Next_Loc
;' --------------------------------
Space_Chr:
LD A,(spc_size);' Skip pixels
LD DE,(scr_ad);' Get screen addr.
POP BC ;' Throw away values
LD B,0
LD C,A
JP Next_Loc2
;' --------------------------------
Next_Loc:
POP BC ;' Move to next char location
Next_Loc2:
LD A,(pix_pos);' =bitpos+x_gap+size
LD L,A
LD A,(x_gap)
ADD A,L
ADD A ,C
LD L ,A
AND 7 ;' New pix_pos
LD (pix_pos),A
LD H,0 ;' =result/8
SRL L
SRL L
SRL L
ADD HL,DE ;' New byte pos
LD A ,E ;' Check for screen end
AND 31
LD E,A
LD A,L
AND 31
CP E ;' New pos smaller than old ?
JP C ,Exit2 ;' -> End of printing
LD (scr_ad),HL ;' Store new scr ad.
POP HL ;' Restore text pointer
JP Main_Loop
;' --------------------------------
Outside:
POP BC ;' Stop printing
POP HL ;' Char not printed!
DEC HL
JP Exit1
;' --------------------------------
Exit2:
POP HL ;' Return nr of printed chars
Exit1:
LD DE,(text_addr)
XOR A
SBC HL,DE
LD B,H ;' Return value in BC to Basic
LD C,L
jp PropPrintTxtadr2
;' --------------------------------
;' Calc. scr adr from x,y
;' --------------------------------
;' In : B = x / C = y (preserved)
;' Out: DE = scr adr / L = pixpos
;' Usd: A, BC, DE, L
;' --------------------------------
PosToScr:
LD A,C ;' Range check
CP 185
JP C,ValOk
LD C,184
ValOk:
LD A,B ;' Pix pos
AND 7
LD L,A
LD E,B
SRL E
SRL E
SRL E
LD A,C ;' Scr pos
AND 7
LD D,A
LD A,C
AND 56
RLA
RLA
OR E
LD E,A
LD A,C
AND 192
RRA
RRA
RRA
OR D
OR 64
LD D,A
RET
;' --------------------------------
;' Copy char into buffer
;' --------------------------------
;' In : A = Char
;' Out: -
;' Usd: A, HL, DE, BC
;' --------------------------------
Copy_Chr:
LD DE,(23606);' SysVar CHARS
LD H,0
LD L,A
ADD HL,HL ;' * 8
ADD HL,HL
ADD HL,HL
ADD HL,DE ;' + Chartable
EX DE,HL
LD HL,Chr_Buf
LD B,8
Copy_Loop:
LD A,(DE);' Double to 16 pixel/row
INC DE
LD (HL),A ;' Low-byte in memory!
INC HL
LD (HL),0 ;' High-byte in memory!
INC HL
DJNZ Copy_Loop
RET
;' --------------------------------
;' Measure left border and width
;' --------------------------------
;' In : -
;' Out: B = left rim / C = width
;' Usd: A, HL, BC
;' --------------------------------
Measure:
LD HL,Chr_Buf ;' "OR" together all 8 bytes
LD B,8
XOR A
Msr_Loop:
OR (HL)
INC HL
INC HL
DJNZ Msr_Loop
LD BC,0
AND A ;' Check if zero
RET Z
Msr_Loop2:
INC B ;' Measure left border
RLCA
JP NC,Msr_Loop2
RRCA
DEC B
LD C,9 ;' Measure width
Msr_Loop3:
DEC C
RRCA
JP NC,Msr_Loop3
RET
;' --------------------------------
;' Move char to left, 8 bit
;' --------------------------------
;' In : B = Nr of bits to shift
;' Out: -
;' Usd: A, B, HL
;' --------------------------------
Chr_Left8:
PUSH BC
LD HL,Chr_Buf ;' Rotate char left 8 bit
LD C,B ;' 1st row
LD A,(HL )
Chr_LLp1:
RLCA
DJNZ Chr_LLp1
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 2nd row
LD A,(HL)
Chr_LLp2:
RLCA
DJNZ Chr_LLp2
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 3rd row
LD A ,(HL)
Chr_LLp3:
RLCA
DJNZ Chr_LLp3
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 4th row
LD A,(HL)
Chr_LLp4:
RLCA
DJNZ Chr_LLp4
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 5th row
LD A,(HL)
Chr_LLp5:
RLCA
DJNZ Chr_LLp5
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 6th row
LD A,(HL)
Chr_LLp6:
RLCA
DJNZ Chr_LLp6
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 7th row
LD A,(HL)
Chr_LLp7:
RLCA
DJNZ Chr_LLp7
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 8th row
LD A,(HL)
Chr_LLp8:
RLCA
DJNZ Chr_LLp8
LD (HL),A
LD B,C
POP BC
RET
;' --------------------------------
;' Move char to right, 8 bit
;' --------------------------------
;' In : B = Nr of bits to shift
;' Out: -
;' Usd: A, B, HL
;' --------------------------------
Chr_Rgt8:
PUSH BC
LD HL,Chr_Buf ;' Rotate char right 8 bit
LD C,B ;' 1st row
LD A,(HL)
Chr_RLp1:
RRCA
DJNZ Chr_RLp1
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 2nd row
LD A,(HL)
Chr_RLp2:
RRCA
DJNZ Chr_RLp2
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 3rd row
LD A,(HL)
Chr_RLp3:
RRCA
DJNZ Chr_RLp3
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 4th row
LD A,(HL)
Chr_RLp4:
RRCA
DJNZ Chr_RLp4
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 5th row
LD A,(HL)
Chr_RLp5:
RRCA
DJNZ Chr_RLp5
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 6th row
LD A ,(HL )
Chr_RLp6:
RRCA
DJNZ Chr_RLp6
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 7th row
LD A,(HL)
Chr_RLp7:
RRCA
DJNZ Chr_RLp7
LD (HL),A
LD B,C
INC HL
INC HL
LD C,B ;' 8th row
LD A,(HL)
Chr_RLp8:
RRCA
DJNZ Chr_RLp8
LD (HL),A
LD B,C
POP BC
RET
;' --------------------------------
;' Move char to right 16 bit
;' --------------------------------
;' In : B = Nr of bits to shift
;' Out:
;' Usd: A, B, HL
;' --------------------------------
Chr_Rgt16:
LD HL,(Chr_Buf);' Rotate char right 16 bit
LD A,B ;' 1st row
Chr_R2Lp1:
SRL L
RR H ;' Insert carry
DJNZ Chr_R2Lp1
LD B,A
LD (Chr_Buf),HL
LD HL,(Chr_Buf+2);' 2nd row
LD A,B
Chr_R2Lp2:
SRL L
RR H ;' Insert carry
DJNZ Chr_R2Lp2
LD B,A
LD (Chr_Buf+2),HL
LD HL,(Chr_Buf+4);' 3rd row
LD A,B
Chr_R2Lp3:
SRL L
RR H ;' Insert carry
DJNZ Chr_R2Lp3
LD B,A
LD (Chr_Buf+4),HL
LD HL,(Chr_Buf+6);' 4th row
LD A,B
Chr_R2Lp4:
SRL L
RR H ;' Insert carry
DJNZ Chr_R2Lp4
LD B,A
LD (Chr_Buf+6),HL
LD HL,(Chr_Buf+8);' 5th row
LD A,B
Chr_R2Lp5:
SRL L
RR H ;' Insert carry
DJNZ Chr_R2Lp5
LD B,A
LD (Chr_Buf+8),HL
LD HL,(Chr_Buf+10);' 6th row
LD A,B
Chr_R2Lp6:
SRL L
RR H ;' Insert carry
DJNZ Chr_R2Lp6
LD B,A
LD (Chr_Buf+10),HL
LD HL,(Chr_Buf+12);' 7th row
LD A,B
Chr_R2Lp7:
SRL L
RR H ;' Insert carry
DJNZ Chr_R2Lp7
LD B,A
LD (Chr_Buf+12),HL
LD HL,(Chr_Buf+14);' 8th row
LD A,B
Chr_R2Lp8:
SRL L
RR H ;'Insert carry
DJNZ Chr_R2Lp8
LD B,A
LD (Chr_Buf+14),HL
RET
;' --------------------------------
;' Print 8 bit wide char on screen
;' --------------------------------
;' In : DE = screen adr.
;' Out: -
;' Usd: A, HL, DE, B
;' --------------------------------
Print8:
LD HL,Chr_Buf
PUSH DE ;' save scr ad.
EX DE,HL
LD B,8 ;' 8 lines
Prt8_L:
LD A,(DE);' set 1 byte
XOR (HL)
LD (HL),A
INC DE ;' skip 1 byte
INC DE ;' next byte
INC H ;' calc. next line
LD A,H
AND 7
JP NZ,Prt8_C
LD A,L
ADD A,32
LD L,A
JR C,Prt8_C
LD A,H
SUB 8
LD H,A
Prt8_C:
DJNZ Prt8_L ;' next round
POP DE ;' restore scr ad.
RET
;'--------------------------------
;' Print 16 bit wide char on screen
;' --------------------------------
;' In : DE = screen adr.
;' Out: -
;' Usd: A, HL, DE, B
;' --------------------------------
Print16:
LD HL,Chr_Buf
PUSH DE ;' save scr ad.
EX DE,HL
LD B,8 ;' 8 lines
Prt16_L:
LD A,(DE);' set 1 byte
XOR (HL)
LD (HL),A
INC HL ;' next scr pos
INC DE ;' next byte
LD A,(DE);' set 1 byte
XOR (HL)
LD (HL),A
DEC HL ;' prev scr pos
INC DE ;' next byte
INC H ;' calc. next line
LD A,H
AND 7
JP NZ,Prt16_C
LD A,L
ADD A,32
LD L,A
JR C,Prt16_C
LD A,H
SUB 8
LD H,A
Prt16_C:
DJNZ Prt16_L ;' next round
POP DE ;' restore scr ad.
RET
;' --------------------------------
;' Variables
;' --------------------------------
Chr_Buf:
DEFS 16
scr_ad:
DEFW 0
pix_pos:
DEFB 0
;' -----------------
PropPrintTxtadr2:
end asm
end sub
Re: Proportional pixel-precise printing anywhere on screen - britlion - 06-02-2010
It's a little ironic that I got hold of some proportional printing code just the other day.
And I was working on a routine to put a character anywhere on the screen (any pixel address).
I guess I can probably drop the PropPrint one. a 64 char one is on my hitlist, though.
Edit: I just tried it. Shouldn't it leave at least a pixel between characters? You might want to consider self modifying code sneakiness as well. I used that with putTile.
Instead of poking to bytes:
POKE uinteger @datastore, data
datastore:
asm
defb 0,0
...
...
...
LD HL, (DATASTORE)
I got it to do self modifying code so that it poked it to the line that has:
LD HL, NNNN
It saves storage bytes, actually - and LD HL, NN is 10 T states. LD HL,(NN) is 16.
Re: Proportional pixel-precise printing anywhere on screen - LCD - 06-03-2010
No, don't dump your code, post it here. This one has 1,5 KB after compilation, so yours is maybe shorter. Please publish it. And we don't have a 64 char/line routine here yet (I ripped the one from tasword II, but it allows only to put text on screen thirds).
If you want to leave a pixel gap between characters, you can change xgap value to from 0 to 1. I wanted just to demonstrate how condensed it can print.
You are right, Your method of poking the values save bytes and time, but at least time is not so important here because this will be done once per line, so we cannot save much time, But thank you for the tip, it will be useful in other programs.
Self modyfing code is dangerous with code optimiser. I made a SUB to put a graphics block on screen, using self modyfing ASM code, but the first version was screwed by the optimiser until I found the reason. The result was a crash of the emulator.
We cannot write this:
Code: LD HL,0
LD DE,0
LD BC,0
LDIR
to modity it later, because optimiser turns it into:
Code: LD HL,0
LD D,H
LD E,L
LD BC,0
LDIR
Which is a bit faster and smaller, but trying to modify the old code will break it.
I wonder why LD BC is not turned into LD B,H:LD C,L too, maybe the compiler does not expect a third equal Register assignation.
I solved this by changing it into:
Code: LD HL,1
LD DE,2
LD BC,3
LDIR
Re: Proportional pixel-precise printing anywhere on screen - boriel - 06-03-2010
LCD Wrote:No, don't dump your code, post it here. This one has 1,5 KB after compilation, so yours is maybe shorter. Please publish it. And we don't have a 64 char/line routine here yet (I ripped the one from tasword II, but it allows only to put text on screen thirds).
If you want to leave a pixel gap between characters, you can change xgap value to from 0 to 1. I wanted just to demonstrate how condensed it can print.
You are right, Your method of poking the values save bytes and time, but at least time is not so important here because this will be done once per line, so we cannot save much time, But thank you for the tip, it will be useful in other programs.
Self modyfing code is dangerous with code optimiser. I made a SUB to put a graphics block on screen, using self modyfing ASM code, but the first version was screwed by the optimiser until I found the reason. The result was a crash of the emulator.
We cannot write this:
Code: LD HL,0
LD DE,0
LD BC,0
LDIR
to modity it later, because optimiser turns it into:
Code: LD HL,0
LD D,H
LD E,L
LD BC,0
LDIR
A compiler directive, #pragma or $something will be created in the future, to mark "non-optimizable" regions.
A workaround for this is:
Code: ASM
DB 21h, 00, 00 ; LD HL, 0
DB 11h, 00, 00 ; LD DE, 0
DB 01h, 00, 00 ; LD BC, 0
LDIR
This could work (the compiler do not optimize DB values), but have not tried it.
Another possibility is disabling optimization for user ASM blocks.
LCD Wrote:Which is a bit faster and smaller, but trying to modify the old code will break it.
I wonder why LD BC is not turned into LD B,H:LD C,L too, maybe the compiler does not expect a third equal Register assignation. It doesn't, but will try to implement it. :wink:
Re: Proportional pixel-precise printing anywhere on screen - programandala.net - 06-18-2010
britlion Wrote:Instead of poking to bytes:
POKE uinteger @datastore, data
datastore:
asm
defb 0,0
...
...
...
LD HL, (DATASTORE)
I got it to do self modifying code so that it poked it to the line that has:
LD HL, NNNN
It saves storage bytes, actually - and LD HL, NN is 10 T states. LD HL,(NN) is 16.
I do that in my Z80 programs. Why have a separate zone for values, if I can store them right into the instructions? Faster and more compact. Maybe the legibility suffers a bit, but it's worthwhile, especially for huge and complex programs where speed and memory are critical.
Re: Proportional pixel-precise printing anywhere on screen - boriel - 06-18-2010
CLS and PRINT routine both uses LD HL, SCREEN_ADDRESS. CLS have LD HL, SCREEN ADDRESS. PRint uses LD HL,(SCREEN_ADDRESS) by reading into CLS's routine. This way:
Code: CLS:
SCREEN_ADDRESS EQU XXXX + 1
XXXX:
LD HL, 16384 ; Defaults to 16384
...
...
PRINT:
LD HL, (SCREEN_ADDRESS)
One might think that this scheme should be reversed so LD HL, SCREEN_ADDRESS goes in print routine (CLS is less frequently used). But this will require to include PRINT (a long routine) even if PRINT instruction is not used. So goes to CLS (much much shorter). See cls.asm and print.asm source codes in librar-asm if you like (PRINT needs some rearrangement).
|