FAQ  •  Register  •  Login

BritLion's High Res Print

<<

britlion

Posts: 766

Joined: Mon Apr 27, 2009 7:26 pm

Location: Slough, Berkshire, UK

Post Sun Jun 20, 2010 4:00 pm

BritLion's High Res Print

Here's what I've been playing with. Weird that someone else was looking into it too. I have to thank Turkwel over at the WorldOfSpectrum Forums for a LOT of guidance with this one.

This routine prints a character at pixel position (x,y); optionally with attributes.

0 < x < 254
0 < y < 191

If the character is < 256, it assumes you sent an ascii code, and pulls it from the character set. UDG ascii code characters aren't supported in this version (use the address of the UDG - eg. USR "a" instead). If the number is 256 - 65535, it assumes this is an address for a user defined block of data (a character sprite/udg) and plots this data to the screen instead.

If the attribute byte is zero, attributes are skipped, and the routine just updates pixels. I thought, since 0 is black on black, this wasn't too bad a compromise. Otherwise it adjusts the attributes to the byte given. Note that printing one character can adjust up to four attributes!

Note also that this is not a megafast solution to printing sprites. I'm still looking into that option.

There is a Newer version of this and a "sprite" example posted later in this thread.
  Code:
FUNCTION t() as uLong
asm
    DI
    LD DE,(23674)
    LD D,0
    LD HL,(23672)
    EI
end asm
end function

SUB HRPrint (x as uByte, y as uByte, char as uInteger, attribute as uByte)
'High res Printing, based on code produced, with thanks, by Turkwel over on the WOS boards.
'Brought to ZX Basic by Britlion, June 2010.

asm
      ld b,(IX+7)
       ld c,(IX+5)
      
      push BC ; save our co-ordinates.
      
;print_char:   
        ld  d,(IX+09)
        inc d
        dec d
        jr z, HRPrint_From_Charset
        ld e,(IX+08)
        jp HR_Print
HRPrint_From_Charset:       
        ld  de,(23606)
      ld  h,0
      ld  l,(IX+8) ; character
      add  hl,hl
      add  hl,hl
      add  hl,hl
      add  hl,de
      
HR_Print:

      call HRPat   
;convert the Y and X pixel values to the correct Screen Address  - Address in DE
      ld a,8
;set counter to 8 - Bytes of Character Data to put down
HRPrint0:
       push af
;save off Counter
      ld a,b
      cp 192
      jr c,HRprint1
       pop af
      jp HRPrintEnd
;don't print character if  > 191 - off the bottom of the screen - restore AF and exit Print routine
;[this can be removed if you are keeping tight control of your Y values]
HRprint1:
       push hl
      push de
      push de
;save off Address of Character Data, Screen Address, Screen Address
      ld a,c
      and 7
      ld d,a
;get lowest 3 bits of Screen address
      ld e,255
;set up E with the Mask to use - 11111111b = All On
      ld a,(hl)
      jr z,HRprint3
;get a Byte of Character Data to put down - but ignore the following Mask shifting
;if the the X value is on an actual Character boundary i.e. there's no need to shift anything
HRprint2:
       rrca
      srl e
      dec d
      jp nz,HRprint2
;Rotate the Character Data Byte D times - and Shift the Mask Byte as well, forcing Zeroes into the
;Left hand side. The Mask will be used to split the Rotated Character Data over a Character boundary
HRprint3:
       pop hl
;POP one of the Screen Addresses (formerly in DE) into HL
      ld d,a
      ld a,e
      and d
      ;or (hl)
      ld (hl),a
;take the Rotated Character Data, mask it with the Mask Byte and the OR it with what's already on the Screen,
;this takes care of the first part of the Byte
;[remove the OR (HL) if you just want a straight write rather than a merge]
      inc l
      ld a,l
      and 31
      jr z,HRprint4
;Increment the Screen Address and check to see if it's at the end of a line,
;if so then there's no need to put down the second part of the Byte
      ld a,e
      cpl
      and d
      ;or (hl)
      ld (hl),a
;Similar to the first Byte, we need to Invert the mask with a CPL so we can put down the second part of the Byte
;in the next Character location
;[again, remove the OR (HL) if you just want a straight write rather than a merge]
HRprint4:
       pop de
      inc d
      inc b
;get the Screen Address back into DE, increment the MSB so it points the the Address immediately below
;it and Increment the Y value in B as well
      ld a,b
      and 7
      call z,HRPat
;now check if the Y value has gone over a Character Boundary i.e. we will need to recalculate the Screen
;Address if we've jumped from one Character Line to another - messy but necessary especially for lines 7 and 15
      pop hl
      inc hl
;get the Address of the Character Data back and increment it ready for the next byte of data
      pop af
      dec a
      jp nz,HRPrint0
;get the Counter value back, decrement it and go back for another write if we haven't reached the end yet
      jp HRPrintAttributes

       
;HRPAT is a subroutine to convert pixel values into an absolute screen address
;On Entry - B = Y Value C = X Value   On Exit - DE = Screen Address
HRPat:
       ld a,b
      srl a
      srl a
      srl a
      ld e,a
      and 24
      or 64
      ld d,a
      ld a,b
      and 7
      add a,d
      ld d,a
      ld a,e
       and  7
      rrca
      rrca
      rrca
      ld e,a
      ld a,c
      srl a
      srl a
       srl a
       add a,e
      ld  e,a
      ret

HRPrintAttributes:
        POP BC ; recover our X-Y co-ordinates.
       ld d,0
      ld a,(IX+11) ; attribute
      and a
      jr z, HRPrintEnd  ; if attribute=0, then we don't do attributes.
      ld e,a ; pass to e
;transfer Attribute Byte to e for easier use
      ld a,b
      cp 192
      jr nc, HRPrintEnd
;check Y position and exit if off bottom of screen
      push bc
;save off Y and X values for later
      and 248
      ld h,22
      ld l,a
      add hl,hl
      add hl,hl
      srl c
      srl c
       srl c
      ld b,d
      add hl,bc
;calculate the correct Attribute Address for the Y\X values
      ld (hl),e
;set the Attribute - this is ALWAYS set no matter what the valid Y\X values used
      pop bc
;get the Y and X values back into BC
      ;call print_attribute2
;call the subroutine to see if an adjacent Horizontal Attribute needs to be set
print_attributes1:
       ld a,c
      cp 248
      jr nc,endPrintAttributes1
;check to see if we are at Horizontal character 31 - if so then no need to set adjacent Horizontal Attribute
      and 7
      jr z, endPrintAttributes1
;and don't set the adjacent Horizontal Attribute if there's no need to
      inc l
      ld (hl),e
      dec l
;increment the Attribute address - set the adjacent horizontal Attribute - then set the Attribute Address back
endPrintAttributes1:                             


;
      ld a,b
      cp 184
      jr nc, HRPrintEnd
;check to see if we are at Vertical character 23 - if so then no need to set adjacent Vertical Attribute & Exit routine
      and 7
      jr z, HRPrintEnd
;and don't set the adjacent Vertical Attribute if there's no need to & Exit routine
      ld a,l
      add a,32
      ld l,a
      ld a,d
       adc a,h
      ld h,a
      ld (hl),e
;set the Attribute address to the line below  - and set the adjacent Vertical Attribute
;
;drop through now into adjacent Horizontal Attribute subroutine - all RETs will now Exit the routine completely
;
HRPrintAttribute2:   ld a,c
      cp 248
      jr nc, HRPrintEnd
;check to see if we are at Horizontal character 31 - if so then no need to set adjacent Horizontal Attribute
      and 7
      jr z, HRPrintEnd
;and don't set the adjacent Horizontal Attribute if there's no need to
      inc l
      ld (hl),e
      dec l
;increment the Attribute address - set the adjacent horizontal Attribute - then set the Attribute Address back

HRPrintEnd:

      
end asm
END SUB


CLS
PRINT at 15,0;"Routine  Test"
time=t()
FOR y=0 to 100
FOR x=0 to 100
HRPrint(x,y,CODE"2",39)
NEXT x
NEXT y
print (t()-time)/CASt(float,50); " seconds"
Last edited by britlion on Sat Sep 10, 2011 1:02 am, edited 3 times in total.
<<

LCD

Posts: 596

Joined: Fri Feb 13, 2009 3:11 pm

Location: Vienna, Austria

Post Sun Jun 20, 2010 7:18 pm

Re: BritLion's High Res Print

Wow, this is very fast. Thanks for the example.
------------------------------------------------------------
http://lcd-one.da.ru redirector is dead
Visit my http://members.inode.at/838331/index.html home page!
<<

britlion

Posts: 766

Joined: Mon Apr 27, 2009 7:26 pm

Location: Slough, Berkshire, UK

Post Sun Jun 20, 2010 9:37 pm

Re: BritLion's High Res Print

It's a little slowed up by the options to do characters, user data and attributes; but not by a huge amount. (Though it is almost 10% faster without attributes).

You asked for flexibility in these routines, so I did it that way this time.
<<

britlion

Posts: 766

Joined: Mon Apr 27, 2009 7:26 pm

Location: Slough, Berkshire, UK

Post Sun Jun 20, 2010 10:46 pm

Re: BritLion's High Res Print

Updated version. This allows you to turn OVER on and off (so you can merge characters without them destroying bits of each other. There's another example included that might have a familiar character in it).

It also shows this is NOT really fast enough for sprites. 8 calls gets flickery...

This version adds the OVER parameter: 0 for overprint, 1 for merge.
  Code:

SUB HRPrint (x as uByte, y as uByte, char as uInteger, attribute as uByte, overprint as uByte)
'High res Printing, based on code produced, with thanks, by Turkwel over on the WOS boards.
'Brought to ZX Basic by Britlion, June 2010.

asm
      ld a,(IX+13)
      AND a
      JR Z,HRP_No_Over
      LD a,182
        JP HRP_Change_Code
HRP_No_Over:
        LD a,0
HRP_Change_Code:     
        LD (HRPOver1),a
        LD (HRPOver2),a
       
        ld b,(IX+7)
       ld c,(IX+5)
      
      push BC ; save our co-ordinates.
      
;print_char:   
        ld  d,(IX+09)
        inc d
        dec d
        jr z, HRPrint_From_Charset
        ld e,(IX+08)
        jp HR_Print
HRPrint_From_Charset:       
        ld  de,(23606)
      ld  h,0
      ld  l,(IX+8) ; character
      add  hl,hl
      add  hl,hl
      add  hl,hl
      add  hl,de
      
HR_Print:

      call HRPat   
;convert the Y and X pixel values to the correct Screen Address  - Address in DE
      ld a,8
;set counter to 8 - Bytes of Character Data to put down
HRPrint0:
       push af
;save off Counter
      ld a,b
      cp 192
      jr c,HRprint1
       pop af
      jp HRPrintEnd
;don't print character if  > 191 - off the bottom of the screen - restore AF and exit Print routine
;[this can be removed if you are keeping tight control of your Y values]
HRprint1:
       push hl
      push de
      push de
;save off Address of Character Data, Screen Address, Screen Address
      ld a,c
      and 7
      ld d,a
;get lowest 3 bits of Screen address
      ld e,255
;set up E with the Mask to use - 11111111b = All On
      ld a,(hl)
      jr z,HRprint3
;get a Byte of Character Data to put down - but ignore the following Mask shifting
;if the the X value is on an actual Character boundary i.e. there's no need to shift anything
HRprint2:
       rrca
      srl e
      dec d
      jp nz,HRprint2
;Rotate the Character Data Byte D times - and Shift the Mask Byte as well, forcing Zeroes into the
;Left hand side. The Mask will be used to split the Rotated Character Data over a Character boundary
HRprint3:
       pop hl
;POP one of the Screen Addresses (formerly in DE) into HL
      ld d,a
      ld a,e
      and d
HRPOver1:      or (hl)
      ld (hl),a
;take the Rotated Character Data, mask it with the Mask Byte and the OR it with what's already on the Screen,
;this takes care of the first part of the Byte
;[remove the OR (HL) if you just want a straight write rather than a merge]
      inc l
      ld a,l
      and 31
      jr z,HRprint4
;Increment the Screen Address and check to see if it's at the end of a line,
;if so then there's no need to put down the second part of the Byte
      ld a,e
      cpl
      and d
HRPOver2:      or (hl)
      ld (hl),a
;Similar to the first Byte, we need to Invert the mask with a CPL so we can put down the second part of the Byte
;in the next Character location
;[again, remove the OR (HL) if you just want a straight write rather than a merge]
HRprint4:
       pop de
      inc d
      inc b
;get the Screen Address back into DE, increment the MSB so it points the the Address immediately below
;it and Increment the Y value in B as well
      ld a,b
      and 7
      call z,HRPat
;now check if the Y value has gone over a Character Boundary i.e. we will need to recalculate the Screen
;Address if we've jumped from one Character Line to another - messy but necessary especially for lines 7 and 15
      pop hl
      inc hl
;get the Address of the Character Data back and increment it ready for the next byte of data
      pop af
      dec a
      jp nz,HRPrint0
;get the Counter value back, decrement it and go back for another write if we haven't reached the end yet
      jp HRPrintAttributes

       
;HRPAT is a subroutine to convert pixel values into an absolute screen address
;On Entry - B = Y Value C = X Value   On Exit - DE = Screen Address
HRPat:
       ld a,b
      srl a
      srl a
      srl a
      ld e,a
      and 24
      or 64
      ld d,a
      ld a,b
      and 7
      add a,d
      ld d,a
      ld a,e
       and  7
      rrca
      rrca
      rrca
      ld e,a
      ld a,c
      srl a
      srl a
       srl a
       add a,e
      ld  e,a
      ret

HRPrintAttributes:
        POP BC ; recover our X-Y co-ordinates.
       ld d,0
      ld a,(IX+11) ; attribute
      and a
      jr z, HRPrintEnd  ; if attribute=0, then we don't do attributes.
      ld e,a ; pass to e
;transfer Attribute Byte to e for easier use
      ld a,b
      cp 192
      jr nc, HRPrintEnd
;check Y position and exit if off bottom of screen
      push bc
;save off Y and X values for later
      and 248
      ld h,22
      ld l,a
      add hl,hl
      add hl,hl
      srl c
      srl c
       srl c
      ld b,d
      add hl,bc
;calculate the correct Attribute Address for the Y\X values
      ld (hl),e
;set the Attribute - this is ALWAYS set no matter what the valid Y\X values used
      pop bc
;get the Y and X values back into BC
      ;call print_attribute2
;call the subroutine to see if an adjacent Horizontal Attribute needs to be set
print_attributes1:
       ld a,c
      cp 248
      jr nc,endPrintAttributes1
;check to see if we are at Horizontal character 31 - if so then no need to set adjacent Horizontal Attribute
      and 7
      jr z, endPrintAttributes1
;and don't set the adjacent Horizontal Attribute if there's no need to
      inc l
      ld (hl),e
      dec l
;increment the Attribute address - set the adjacent horizontal Attribute - then set the Attribute Address back
endPrintAttributes1:                             


;
      ld a,b
      cp 184
      jr nc, HRPrintEnd
;check to see if we are at Vertical character 23 - if so then no need to set adjacent Vertical Attribute & Exit routine
      and 7
      jr z, HRPrintEnd
;and don't set the adjacent Vertical Attribute if there's no need to & Exit routine
      ld a,l
      add a,32
      ld l,a
      ld a,d
       adc a,h
      ld h,a
      ld (hl),e
;set the Attribute address to the line below  - and set the adjacent Vertical Attribute
;
;drop through now into adjacent Horizontal Attribute subroutine - all RETs will now Exit the routine completely
;
HRPrintAttribute2:   ld a,c
      cp 248
      jr nc, HRPrintEnd
;check to see if we are at Horizontal character 31 - if so then no need to set adjacent Horizontal Attribute
      and 7
      jr z, HRPrintEnd
;and don't set the adjacent Horizontal Attribute if there's no need to
      inc l
      ld (hl),e
      dec l
;increment the Attribute address - set the adjacent horizontal Attribute - then set the Attribute Address back
      ;ret                       

HRPrintEnd:
      
end asm
END SUB

CLS

DIM x,y as uByte
DIM xd,yd as byte
x=100
y=10
xd=1
yd=1

DO
PAUSE 2
HRPrint(x,y,32,56,0)
HRPrint(x+8,y,32,56,0)
HRPrint(x,y+8,32,56,0)
HRPrint(x+8,y+8,32,56,0)
x=x+xd
y=y+yd
HRPrint(x,y,@gentle,76,0)
HRPrint(x+8,y,@gentle+8,76,1)
HRPrint(x,y+8,@gentle+16,76,0)
HRPrint(x+8,y+8,@gentle+24,76,1)

IF x<=0 OR x>=247 THEN xd=-xd : END IF
IF y<=0 OR y>=184 THEN yd=-yd : END IF

LOOP
end

gentle:
asm
defb 15,15,15,15,15,15,13,15
defb 240,144,208,208,240,240,176,240
defb 15,14,63,0,0,12,26,30
defb 176,112,252,0,0,24,104,120
end asm
<<

LCD

Posts: 596

Joined: Fri Feb 13, 2009 3:11 pm

Location: Vienna, Austria

Post Sun Jun 20, 2010 11:42 pm

Re: BritLion's High Res Print

It flickers a lot in the upper screen area, but who cares, after it is a real Hires Print routine. This is the next thing to a real sprite routine. Thanks for it!
------------------------------------------------------------
http://lcd-one.da.ru redirector is dead
Visit my http://members.inode.at/838331/index.html home page!
<<

britlion

Posts: 766

Joined: Mon Apr 27, 2009 7:26 pm

Location: Slough, Berkshire, UK

Post Sun Jun 20, 2010 11:55 pm

Re: BritLion's High Res Print

This, fourspriter...I'm getting better at graphics, and looking over z88dk's sp1 library still.

Maybe all that will come together :-)
<<

compiuter

Posts: 51

Joined: Mon May 10, 2010 10:47 pm

Post Mon Jun 21, 2010 6:27 am

Re: BritLion's High Res Print

Thx for the new very practical methods.
<<

boriel

Site Admin

Posts: 1463

Joined: Wed Nov 01, 2006 6:18 pm

Location: Santa Cruz de Tenerife, Spain

Post Mon Jun 21, 2010 6:40 am

Re: BritLion's High Res Print

You all should realise that most game programs use TOP screen zone (the 1st 32 lines), for displaying scores, titles, etc? :idea:
Maybe flicker is ok if it only happens at the top of the screen (just don't draw anything there). :|
<<

britlion

Posts: 766

Joined: Mon Apr 27, 2009 7:26 pm

Location: Slough, Berkshire, UK

Post Mon Jun 21, 2010 7:01 am

Re: BritLion's High Res Print

I'd considered it :-)

It's flickery for about 2/3 of the screen tho. So...if we're happy with one 2X2 sprite, and we have a 2/3 of the screen status display, and our one sprite in the bottom third...

We might actually be okay!

* :twisted: * :mrgreen:
<<

britlion

Posts: 766

Joined: Mon Apr 27, 2009 7:26 pm

Location: Slough, Berkshire, UK

Post Tue Sep 06, 2011 1:01 am

Re: BritLion's High Res Print

Working on improvements to this.

Again, it's a call between speed and size - this version is enormous at first glance, in that it has 7 * 512 byte rotate tables, and 512 bytes of screen lookup tables, for a lookup table size of 4K.

It does shave almost 20% off the time taken to make one call to the function, however - and remember that the example 16*16 sprite requires 8 calls to update the position, (4 things blanked off, and 4 new characters drawn). Still some flicker, but it's pushed quite a lot higher up the screen.

There are probably still quite a lot of speed optimizations that could be made out of this - I ran out of time, here.

Note also code is quite long - probably better to #include it than insert it directly. Those tables take up a lot of lines.
Attachments
HRPrintFast.zip
(14.41 KiB) Downloaded 192 times
<<

britlion

Posts: 766

Joined: Mon Apr 27, 2009 7:26 pm

Location: Slough, Berkshire, UK

Post Fri Sep 09, 2011 10:03 am

Re: BritLion's High Res Print

That's depressing. All day coding, and not one comment. :(
<<

boriel

Site Admin

Posts: 1463

Joined: Wed Nov 01, 2006 6:18 pm

Location: Santa Cruz de Tenerife, Spain

Post Fri Sep 09, 2011 10:28 am

Re: BritLion's High Res Print

I'm here (BTW glad to see you alive and kicking :!: )
I'm currently at work, and this week is also the National Conference on Parallel Computing at my CS Faculty :P
So I'm afraid I won't be able to answer until weekend. I will have a look at your code then: maybe it can be integrated in the 1.2.8 library?
<<

compiuter

Posts: 51

Joined: Mon May 10, 2010 10:47 pm

Post Fri Sep 09, 2011 2:37 pm

Re: BritLion's High Res Print

I´m rising :mrgreen:
I was testing your routine and it likes me.
I want to mix it with machine coder and
try to make a little game.
The idea is mix it in a tile routine with attribute scanning and in basic move my tile.
Thx for your routine.
<<

boriel

Site Admin

Posts: 1463

Joined: Wed Nov 01, 2006 6:18 pm

Location: Santa Cruz de Tenerife, Spain

Post Tue Sep 13, 2011 8:14 am

Re: BritLion's High Res Print

britlion wrote:Working on improvements to this.
It does shave almost 20% off the time taken to make one call to the function, however - and remember that the example 16*16 sprite requires 8 calls to update the position, (4 things blanked off, and 4 new characters drawn). Still some flicker, but it's pushed quite a lot higher up the screen.

There are probably still quite a lot of speed optimizations that could be made out of this - I ran out of time, here.

I have heard of using PUSH to POKE memory faster (supossedly faster than LD (HL), ... sequence).
<<

britlion

Posts: 766

Joined: Mon Apr 27, 2009 7:26 pm

Location: Slough, Berkshire, UK

Post Thu Sep 15, 2011 9:59 am

Re: BritLion's High Res Print

boriel wrote:
I have heard of using PUSH to POKE memory faster (supossedly faster than LD (HL), ... sequence).


Oh certainly, yes - if you look at some of the other stuff I've done, I've used it. The 2X2 tile print thing was done that way, as was one version of fourspriter. It's got potential.

This routine optionally uses xor to do OVER1 printing, which you need to use to put down multiple characters. I suppose you could build all the blocks in a buffer, but push/pop is best for a linear copy (similar to ldir - pop from one place, then push to another. If the stack pointer goes the right way, that helps too. Have to remember that half of the sequence it probably goes backwards. I think some people have even had data stored in reverse order to work with this.

Anyway, push doesn't really play nicely with AND/OR/XOR of data, so probably not the best choice for this routine. Works well for anything that doesn't mind what it over writes, and for anything that fits perfectly into character squares as well. So long as they are 2 bytes (or multiple of 2) wide! (push is 16 bits).
Next

Return to How-To & Tutorials

Who is online

Users browsing this forum: No registered users and 2 guests

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by Vjacheslav Trushkin for Free Forums/DivisionCore.

phpBB SEO