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)