Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Proportional pixel-precise printing anywhere on screen
#1
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
------------------------------------------------------------
http://lcd-one.da.ru redirector is dead
Visit my http://members.inode.at/838331/index.html home page!
Reply
#2
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.
Reply
#3
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
------------------------------------------------------------
http://lcd-one.da.ru redirector is dead
Visit my http://members.inode.at/838331/index.html home page!
Reply
#4
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:
Reply
#5
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.
Reply
#6
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).
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)