News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_Andy Coates

Compiled sprites

Started by Andy Coates, 11:25, 05 April 16

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Andy Coates

Recently I found an old note pad that I used to scribble ideas down for programming the Amstrad.


On the wiki there was a routine for fast compiled sprites:


LD HL,screen address
LD BC,#800+[most used byte]
LD DE,#C050
LD (HL),byte1:INC HL    ;plot line 1
LD (HL),byte2:INC HL
LD (HL),C
LD A,H:ADD B:LD H,A
JR NC,line2
ADD HL,DE
.line2
LD (HL),byte6:DEC HL    ;plot line 2
LD (HL),C    :DEC HL
LD (HL),byte4
LD A,H:ADD B:LD H,A
JR NC,line3
ADD HL,DE
.line3
LD A,(HL):AND #55:OR byte7:LD (HL),A:INC HL    ;plot line 3 (contains transparent areas)
LD (HL),byte8:INC HL
LD A,(HL):AND #AA:OR byte9:LD (HL),A
RET


This was pretty much the same as the one I did, except that I sacrificed 1024 bytes for a look up table to calculate the next line address which allowed the problem area for hardware scrolling to be dealt with ease. The reason why the table was 1024 bytes was that it catered for two screens worth of offsets that allowed for the wrapping of the hardware scroll. This was all designed for a Mission Genocide type of scroll so the problem area never caused a problem. So what I did was calculate which line you were on for the hardware scroll and load the Stack Pointer to that position and the code basically did this:

LD HL,Screen address
LD SP, Current line for sprite in offset table
LD BC, Most used bytes stored in B and C

LD (HL),byte1:INC L    ;plot line 1
LD (HL),byte2:INC L
LD (HL),C
POP DE
ADD HL,DE

LD (HL),byte6:DEC L    ;plot line 2
LD (HL),C    :DEC L
LD (HL),B
LD A,H:ADD B:LD H,A
POP DE
ADD HL,DE

LD A,(HL):AND #55:OR byte7:LD (HL),A:INC L    ;plot line 3 (contains transparent areas)
LD (HL),byte8:INC L
LD A,(HL):AND #AA:OR byte9:LD (HL),A

RET

I'm curious if there are any faster methods than this? By doing this I managed to get quite a lot of sprites on a vertical scrolling screen. From what I remember of the tech demo I did was that I never used interrupts and used Halt to time things up and basically raced the monitors Electron Beam when updating the sprites. The sprite updates started when the rupture happened at the bottom of the screen. Anyway all good fun remembering all this :)

Just edited the example I gave. Instead of INC HL I do INC L... I basically block copied the Wiki example and forgot to change that.

sigh

So was this a single pixel vertical scroll like Mission Genocide?

There was a recent thread on how many bullets/sprites could be achieved on a vertical shootem up, where Axelay did a nice little demo.

Did you get much further with your routine as I'm sure your input would be valuable.

How many bullets can be drawn on screen?

Andy Coates

That thread was an interesting read... It's very hard to remember that far back. Basically when mission genocide was released I reversed engineered it to find out how it achieved vertical scrolling and got rid of the firmware stuff. Then I did my compiled sprite routines and had that working over the scrolling backdrop. Then I changed computers and never looked at it again until I found that note pad.


According to what I wrote in it I had plans to do a Uridium type of game but the scroll was vertical instead of horizontal. So the scroll would of catered for up and down movement. I had plans to use 16k for that look-up table, storage for the compiled sprites and the score panel. 16k used for the screen and the other 32k used for the rest of the game. I wanted to use all the palette for graphics to get a better look than the C64 graphics.


It's funny reading through all the Amstrad knowledge base now with all the different techniques discovered, so armed with this now I could probably do a better job than I did on that tech demo all those years ago. If only I could find time I would not mind doing that Uridium type of game now because it would prove the Amstrad CPC was a cool machine for the time and it would actually make the C64 look pale in comparison because the Amstrad would certainly have cooler graphics by using 16 colours and it would look arcade looking because of the portrait look of the scroll. But we can only dream :)


Speccy ports basically ruined the potential of the CPC.

arnoldemu

@Andy Coates

For mission genocide the "problem area" is never a problem it is always on the left and never moves from the left because the width of the screen (64 bytes) divides 2048 exactly and because the scrolling is always vertical. Quick sprite drawing is much less of a problem.

The problem is when the screen is scrolled horizontally more than a certain amount. So more a problem of continuous hardware horizontal scrolling. (Some games can avoid it by shrinking the height of the screen and limiting scrolling left and right). The problem shows up plotting each line of the sprite because potentially a line may or may not be on that problem area. The problem being when the byte to the right of &C7FF is not &C7FF+1, but &C000. It's a horizontal and not vertical problem.

Have you looked at a way to do compiled sprites that works with that?

Also your use of INC L only will not work with horizontally scrolled screens as far as I can tell.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Andy Coates

Quote from: arnoldemu on 13:00, 05 April 16
@Andy Coates

For mission genocide the "problem area" is never a problem it is always on the left and never moves from the left because the width of the screen (64 bytes) divides 2048 exactly and because the scrolling is always vertical. Quick sprite drawing is much less of a problem.

The problem is when the screen is scrolled horizontally more than a certain amount. So more a problem of continuous hardware horizontal scrolling. (Some games can avoid it by shrinking the height of the screen and limiting scrolling left and right). The problem shows up plotting each line of the sprite because potentially a line may or may not be on that problem area. The problem being when the byte to the right of &C7FF is not &C7FF+1, but &C000. It's a horizontal and not vertical problem.

Have you looked at a way to do compiled sprites that works with that?

Also your use of INC L only will not work with horizontally scrolled screens as far as I can tell.


Thanks ArnoldEmu :) This was all for a vertical scroll. What I meant for the problem area was that it catered for the wrap problem of hardware scrolling. The look-up table basically gave you the offset to the next line with the wrap correction built in so just by doing:


POP DE
ADD HL,DE


Solves all the problems with the Amstrads strange screen layout and the wrap around calculation. Plus it's quicker and avoids the JR instruction and the usual screen address fixes for the next line. It's actually 4 bytes shorter than the Wiki example. But of course the look-up table adds in 1024 bytes. Probably the more compiled sprites added in would get that table back with the 4 byte saving.

Andy Coates

Actually looking at the Wiki fast sprite stuff it accrued to me that you could speed up the general purpose transparent sprite routine using a lookup table to calculate the next line. Using the same restrictions as the 32 char mode 1 screen you could do this. If no scrolling is used you could use a 512byte look-up table for a screen of 256 pixels high or if vertical hardware scrolling is used the 1024 byte table. Each look-up address has an offset to add to the high part of the byte to the screen. And by using:


POP AF
ADD D
LD D,A


Will get the correct next line. The pop brings in the data to use for A and the F part not used. So that routine becomes:


;Entry: BC = Sprite address, D = width, E = height, HL = screen address

; Set the stack pointer to the right part in the look-up table. Needs code added to do this

LD LX,D  ;LX = width
LD HY,E  ;HY = height
EX DE,HL
LD H,andmasks / 256
.rowloop
LD HX,LX ;HX <= width
.dobyte
LD A,(BC):INC C
LD L,A:LD A,(DE)
AND (HL):INC H:OR (HL):DEC H
LD (DE),A:INC E
DEC HX
JR NZ,dobyte
DEC HY
RET Z
LD A,E
SUB LX
LD E,A

POP AF
ADD D
LD D,A

JR rowloop


So we have pretty much removed all the code to calculate the next line.

I'm having some fun thinking in z80 again :) I'm now really temped to play around with an emulator now!

Andy Coates

#6
Actually there is a bug here because of the RET Z because the stack pointer is been mis-used. Just rearrange the loop to remove the problem as I cannot be bothered now. But the idea is pretty sound still.


I decided to correct it:

;Entry: BC = Sprite address, D = width, E = height, HL = screen address

LD LX,D  ;LX = width
LD HY,E  ;HY = height
EX DE,HL
LD H,andmasks / 256
.rowloop
LD HX,LX ;HX <= width
.dobyte
LD A,(BC):INC C
LD L,A:LD A,(DE)
AND (HL):INC H:OR (HL):DEC H
LD (DE),A:INC E
DEC HX
JR NZ,dobyte

LD A,E
SUB LX
LD E,A

POP AF
ADD D
LD D,A

DEC HY
JR NZ, rowloop

;Set stack pointer back

RET


Actually reading the Wiki on fast sprites this is actually hinted at  :doh:  I think I'll stop re-inventing the wheel because I guess after 30 years programmers have tried every trick in the book. But it was fun thinking about the problem... I just realised I miss programming in assembly language after using C++ for years.

Andy Coates

Out of completeness, I realised that what I did in that note pad and the demo I did all those years ago was not as fast as it could of been. This is the new revised version. People have probably already thought of this for compiled sprites with the interrupts turned off years ago, including me. But I'm pretty shocked I never did this and replaced:


POP DE
ADD HL,DE


with:


POP AF
LD H,A


And to boot BC and DE can hold the most used bytes. Anyway no more nostalgia and re-inventing the wheel. And here's the routine which is probably the fastest it can go:


; Inputs
LD DE, Screen address
LD HL, Current line for sprite in offset table
; Output destroys all

.Sprite
LD (nn),SP
LD SP,HL
EX DE,HL

LD BC, Most used bytes stored in B and C
LD DE, Most used bytes stored in D and E

LD (HL),byte1:INC L    ;plot line 1
LD (HL),D:INC L
LD (HL),C
POP AF
LD H,A

LD (HL),byte6:DEC L    ;plot line 2
LD (HL),C    :DEC L
LD (HL),B
LD A,H:ADD B:LD H,A
POP AF
LD H,A

LD A,(HL):AND #55:OR byte7:LD (HL),A:INC L    ;plot line 3 (contains transparent areas)
LD (HL),E:INC L
LD A,(HL):AND #AA:OR byte9:LD (HL),A

LD SP,(nn)

RET

sigh

it would be really interesting to see this in action on a simple scrolling background with as many bullets on screen.

Executioner

Quote from: Andy Coates on 17:22, 05 April 16
POP DE
ADD HL,DE

with:

POP AF
LD H,A

Well that's never going to work on the 7th scan line of a character is it! Stay with your original POP DE:ADD HL,DE, and the lookup table only has to be 526 bytes since there's only ever one wrap around case all the other values will be either #800 or #c840, except the one which is #c040. For that, you can change one byte in the table from #c8 to #c0 depending on the current scroll position.

You don't have to modify H at all before doing that, so it's simply:


ld (hl),byte
pop de
add hl,de
ld (hl),byte


etc.

Now to optimise it a little further (since your sprite is likely to be more that 1 byte wide), alternate between increment and decrement of L, so your compiled sprite code looks something like:


ld (hl),byte:inc l
ld (hl),byte:inc l
ld (hl),byte:inc l
ld (hl),byte
pop de
add hl,de
ld (hl),byte:dec l
ld (hl),byte:dec l
ld (hl),byte:dec l
ld (hl),byte
pop de
add hl,de


Substituting ld (hl),byte with the appropriate thing, ie. ld (hl),r/ld (hl),n or nothing for blank, or if you want to use masks: ld a,(hl):and mask:or value:ld (hl),a.

Clipping at the left and right edges is always going to be a problem with compiled sprites, there are only really a few options:

1. Create separate compiled sprites for each clipped version (good if you have few sprite types and they're not too wide).
2. Self-modify your compiled sprite code (this is likely to counter the benefit of compiled sprites altogether).
3. Make your display area wider than the actual display (this is probably okay if you're not scrolling as you can control where the page boundaries lie, but unless you can manage 128 bytes wide it's not going to be easy to avoid splits in the middle of the display. The only way to use 128 bye wide screen is adjust horizontal total to 65 which probably shouldn't be done).
4. Write a separate non-compiled version that can cater for all left/right clipping.
5. Probably the best option is to not bother. If the sprite x < 0 or > screen_width - width then don't display it.

Vertical clipping is also a bit of a pain with compiled sprites. You can use some of the above ideas, or have a list of code offsets into the compiled sprite code for top clipping and perhaps self-modify with a RET for bottom clipping. Another simple idea is not to use 256 lines of display and just draw it on the bits that aren't currently displayed (similar to option 3 above, but somewhat easier vertically).

Andy Coates

Quote from: Executioner on 05:24, 06 April 16
Well that's never going to work on the 7th scan line of a character is it! Stay with your original POP DE:ADD HL,DE, and the lookup table only has to be 526 bytes since there's only ever one wrap around case all the other values will be either #800 or #c840, except the one which is #c040. For that, you can change one byte in the table from #c8 to #c0 depending on the current scroll position.


I'm embarrassed to say you are correct - this is what happens when you are tired, late at night. I do like your idea for shortening the look-up table! Clipping on the horizontal will always be the problem with compiled sprites. In my original demo I never dealt with it but for a game it will have to be dealt with. The sensible thing to do would have a normal transparent sprite draw routine and switch to that when any sprites need to be horizontally clipped. The downside is you need the cater for the graphics again but if you compiled in the clipping that would waste bytes so the extra data for for a traditional sprite routine maybe ok.


Andy Coates

Quote from: sigh on 21:55, 05 April 16
it would be really interesting to see this in action on a simple scrolling background with as many bullets on screen.


I am tempted to give it ago again. I might allocate a few evenings to this because I had fun looking at z80 again.

Rhino

#12
Because most of the time you only add #800, I think you can save some speed this way:



draw_sprite
    ld de,#800
    ld bc,#c840
    ld (hl),byte:inc l
    ld (hl),byte:inc l
    ld (hl),byte:inc l
    ld (hl),byte
line0     
    add hl,de
    ld (hl),byte:dec l
    ld (hl),byte:dec l
    ld (hl),byte:dec l
    ld (hl),byte
line1
    add hl,de
    ...




And before call it, poke an "add hl,bc" in the correct linex labels, and restore the "add hl,de" after.
For example, for sprites of 17 pixels height, you can have this to set the scanline skips:



setSpriteSkips_0
    ld    (line0),a
    ld    (line8),a
    ...

setSpriteSkips_1
    ld    (line1),a
    ld    (line9),a
    ...

setSpriteSkips_7
    ld    (line7),a
    ld    (line15),a
    ...



And call to setSpriteSkips_X according the Y of the sprite with a=#9 before, and #19 after call the draw routine

Or you can use "add hl,sp" instead of "add hl,bc" to have bc for common bytes. And for sprites + vertical scrolling, sp = #c040, and poke it when necessary.

Andy Coates

#13
Quote from: Rhino on 13:14, 06 April 16
Because most of the time you only add #800, I think you can save some speed this way:



draw_sprite
    ld de,#800
    ld bc,#c840
    ld (hl),byte:inc l
    ld (hl),byte:inc l
    ld (hl),byte:inc l
    ld (hl),byte
line0     
    add hl,de
    ld (hl),byte:dec l
    ld (hl),byte:dec l
    ld (hl),byte:dec l
    ld (hl),byte
line1
    add hl,de
    ...




And before call it, poke an "add hl,bc" in the correct linex labels, and restore the "add hl,de" after.
For example, for sprites of 17 pixels height, you can have this to set the scanline skips:



setSpriteSkips_0
    ld    (line0),a
    ld    (line8),a
    ...

setSpriteSkips_1
    ld    (line1),a
    ld    (line9),a
    ...

setSpriteSkips_7
    ld    (line7),a
    ld    (line15),a
    ...



And call to setSpriteSkips_X according the Y of the sprite with a=#9 before, and #19 after call the draw routine

Or you can use "add hl,sp" instead of "add hl,bc" to have bc for common bytes. And for sprites + vertical scrolling, sp = #c040, and poke it when necessary.


That's another great idea!


I think I'm convincing myself to give this a go now. Nostalgia has gripped me. So today I was thinking about sprite updates and racing the Electron Beam for a flicker free update. My original demo did not really use the interrupts... I just had a simple one that returned and used Halts to time with. In retrospect this was bloody awful to manage but it was workable. So today I was thinking that you would use the interrupts for the scroll and rupture stuff, plus a sprite management system. That way you know which interrupt is safe to remove and update sprites in regions. The only down side is that you need to be wary of not over running critical parts for the timings of the scroll and rupture with sprite updates.


Just wondering what peoples thoughts are on this and the best approach? I think updating the sprites in an interrupt means I can abuse the Stack Pointer without fear of an interrupt kicking in.


I have a feeling this thread will branch off topic  :P

Rhino

Quote from: Andy Coates on 15:18, 06 April 16

That's another great idea!


I think I'm convincing myself to give this a go now. Nostalgia has gripped me. So today I was thinking about sprite updates and racing the Electron Beam for a flicker free update. My original demo did not really use the interrupts... I just had a simple one that returned and used Halts to time with. In retrospect this was bloody awful to manage but it was workable. So today I was thinking that you would use the interrupts for the scroll and rupture stuff, plus a sprite management system. That way you know which interrupt is safe to remove and update sprites in regions. The only down side is that you need to be wary of not over running critical parts for the timings of the scroll and rupture with sprite updates.


Just wondering what peoples thoughts are on this and the best approach? I think updating the sprites in an interrupt means I can abuse the Stack Pointer without fear of an interrupt kicking in.


I have a feeling this thread will branch off topic  :P


Do you not have memory for a double buffer? :)


Rhino

Deepening in the alternative proposed before, this can be a compiled sprite implementation when scroll is not involved:



    ; hl = draw a compiled sprite routine
    ; ix = back address

draw_compiled_sprite_9_to_17_height
    ld    sp,hl

    ; calculate the screen address from the sprite coords

    ; ...

    ; hl = draw_sprite_compiled_routine
    ; de = screen adress
    ;  a = y coord

    exx
    and    7
    add    a    ; x2
    add    a    ; x4
    sub    8*4
    ld    l,a
    ld    h,#ff
    add    hl,sp
    ld    sp,hl
    pop    hl
    pop    de
    ld    a,#39    ; #39 = add hl,sp
    ld    (hl),a
    ld    (de),a
    exx

    ld    sp,#c840
    jp    (hl)        ; jump to the draw routine

unused_line_skip
    db    0


    ; Example of compiled sprite

    dw    line_skip8    ; -32
    dw    line_skip16    ; use unused_line_skip if not need (height < 17)
    dw    line_skip7    ; -28
    dw    line_skip15    ; use unused_line_skip if not need (height < 17)
    dw    line_skip6    ; -24
    dw    line_skip14    ; use unused_line_skip if not need (height < 17)
    dw    line_skip5    ; -20
    dw    line_skip13    ; use unused_line_skip if not need (height < 17)
    dw    line_skip4    ; -16
    dw    line_skip12    ; use unused_line_skip if not need (height < 17)
    dw    line_skip3    ; -12
    dw    line_skip11    ; use unused_line_skip if not need (height < 17)
    dw    line_skip2    ; -8
    dw    line_skip10    ; use unused_line_skip if not need (height < 17)
    dw    line_skip1    ; -4
    dw    line_skip9

draw_a_compiled_sprite_17h
    ex    hl,de        ; hl = screen adress
    ld    de,#800
    ld    bc,#xxxx    ; 2 common bytes
   
    ld     (hl),byte:inc l
    ld     (hl),byte:inc l
    ld     (hl),byte:inc l
    ld     (hl),byte
line_skip1
    add     hl,de
    ld     (hl),byte:dec l
    ld     (hl),byte:dec l
    ld     (hl),byte:dec l
    ld     (hl),byte
line_skip2
    add     hl,de

    ...

line_skip16
    add     hl,de
    ld     (hl),byte:inc l
    ld     (hl),byte:inc l
    ld     (hl),byte:inc l
    ld     (hl),byte

    exx
    ld    a,#19        ; #19 = add hl,de
    ld    (hl),a
    ld    (de),a

    jp    (ix)            ; back



If scrolling, pop de: add hl,de may be a better solution if there are many repeated bytes in the sprites, because you have bc free for it.


Andy Coates

Quote from: Rhino on 19:45, 06 April 16

Do you not have memory for a double buffer? :)


If this actually turns into a game then all memory will be valuable and double buffering the screen is wasted memory. That extra 16k will be valuable for graphics and such. The good thing about doing a vertical hardware scroll game is the Amstrad is very suited in hardware to do that really well and it would be nice to get a really polished 2 way vertical scroll working to show off what the machine is actually capable off. I can imagine a defender type of scroll with nice inertia and really good pixel art using the 16 colours to max. Now that's something a C64 could never achieve - loads of colours and an arcade looking screen size. The game would be sort of be like Uridium but with more depth in game play.


I think I'm getting serious about doing this now as a hobby and I want to aim high  ;)  And I do have the experience to pull this off because I've been a games coder for over 25 years.


So this evening using WinApe I've got ruptures working top and bottom with the play area in the middle. I'd forgotten how much fun programming low level is!


I'm not committing to a game yet. But at the moment I'm working on a scrolling demo with sprites... just to see what is possible.







Andy Coates

#17
Quote from: Rhino on 20:26, 06 April 16
Deepening in the alternative proposed before, this can be a compiled sprite implementation when scroll is not involved:



    ; hl = draw a compiled sprite routine
    ; ix = back address

draw_compiled_sprite_9_to_17_height
    ld    sp,hl

    ; calculate the screen address from the sprite coords

    ; ...

    ; hl = draw_sprite_compiled_routine
    ; de = screen adress
    ;  a = y coord

    exx
    and    7
    add    a    ; x2
    add    a    ; x4
    sub    8*4
    ld    l,a
    ld    h,#ff
    add    hl,sp
    ld    sp,hl
    pop    hl
    pop    de
    ld    a,#39    ; #39 = add hl,sp
    ld    (hl),a
    ld    (de),a
    exx

    ld    sp,#c840
    jp    (hl)        ; jump to the draw routine

unused_line_skip
    db    0


    ; Example of compiled sprite

    dw    line_skip8    ; -32
    dw    line_skip16    ; use unused_line_skip if not need (height < 17)
    dw    line_skip7    ; -28
    dw    line_skip15    ; use unused_line_skip if not need (height < 17)
    dw    line_skip6    ; -24
    dw    line_skip14    ; use unused_line_skip if not need (height < 17)
    dw    line_skip5    ; -20
    dw    line_skip13    ; use unused_line_skip if not need (height < 17)
    dw    line_skip4    ; -16
    dw    line_skip12    ; use unused_line_skip if not need (height < 17)
    dw    line_skip3    ; -12
    dw    line_skip11    ; use unused_line_skip if not need (height < 17)
    dw    line_skip2    ; -8
    dw    line_skip10    ; use unused_line_skip if not need (height < 17)
    dw    line_skip1    ; -4
    dw    line_skip9

draw_a_compiled_sprite_17h
    ex    hl,de        ; hl = screen adress
    ld    de,#800
    ld    bc,#xxxx    ; 2 common bytes
   
    ld     (hl),byte:inc l
    ld     (hl),byte:inc l
    ld     (hl),byte:inc l
    ld     (hl),byte
line_skip1
    add     hl,de
    ld     (hl),byte:dec l
    ld     (hl),byte:dec l
    ld     (hl),byte:dec l
    ld     (hl),byte
line_skip2
    add     hl,de

    ...

line_skip16
    add     hl,de
    ld     (hl),byte:inc l
    ld     (hl),byte:inc l
    ld     (hl),byte:inc l
    ld     (hl),byte

    exx
    ld    a,#19        ; #19 = add hl,de
    ld    (hl),a
    ld    (de),a

    jp    (ix)            ; back



If scrolling, pop de: add hl,de may be a better solution if there are many repeated bytes in the sprites, because you have bc free for it.


More cool ideas :) Thanks for your input in all this.


Wow that's some serious Z80 coding there. It took me awhile to get my head  around that!

Rhino

Quote from: Andy Coates on 20:35, 06 April 16

If this actually turns into a game then all memory will be valuable and double buffering the screen is wasted memory. That extra 16k will be valuable for graphics and such. The good thing about doing a vertical hardware scroll game is the Amstrad is very suited in hardware to do that really well and it would be nice to get a really polished 2 way vertical scroll working to show off what the machine is actually capable off. I can imagine a defender type of scroll with nice inertia and really good pixel art using the 16 colours to max. Now that's something a C64 could never achieve - loads of colours and an arcade looking screen size. The game would be sort of be like Uridium but with more depth in game play.


I think I'm getting serious about doing this now as a hobby and I want to aim high  ;)  And I do have the experience to pull this off because I've been a games coder for over 25 years.


So this evening using WinApe I've got ruptures working top and bottom with the play area in the middle. I'd forgotten how much fun programming low level is!


I'm not committing to a game yet. But at the moment I'm working on a scrolling demo with sprites... just to see what is possible.


Nice to see you so motivated! :)

Another idea to optimize large sprites is skip lines with inc h.

For example, for a sprite of 65 pixel height:


    draw line 0...

    inc    h

    draw line 32...

    add    hl,de    ; de = #800-#100

    draw line 1...

    inc    h

    draw line 33...

    add    hl,de
...

Note that this skip lines in 2 nops of average aprox, when usually it take 6 nops or more.

The previous implementation adapted for large sprites would be:





    ; hl = draw_sprite_compiled_routine
    ; ix = back address

draw_compiled_sprite_65_height
    ld    sp,hl

    ; calculate the screen address from the sprite coords

    ; ...

    ; hl = draw_sprite_compiled_routine
    ; de = screen adress
    ;  a = y coord

    exx
    and    7
    ld    l,a
    add    a    ; x2
    add    l    ; x3
    add    a    ; x6
    sub    8*6
    ld    l,a
    ld    h,#ff
    add    hl,sp
    ld    sp,hl
    pop    hl
    pop    de
    pop    bc
    ld    a,#39    ; #39 = add hl,sp
    ld    (hl),a
    ld    (de),a
    ld    (bc),a
    exx

    ld    sp,#c840-#100
    jp    (hl)        ; jump to the draw routine

unused_line_skip
    db    0


    ; Example of compiled sprite


    dw    line_skip8
    dw    line_skip16
    dw    line_skip24

    dw    line_skip7
    dw    line_skip15
    dw    line_skip23

    dw    line_skip6
    dw    line_skip14
    dw    line_skip22

    dw    line_skip5
    dw    line_skip13
    dw    line_skip21

    dw    line_skip4
    dw    line_skip12
    dw    line_skip20

    dw    line_skip3
    dw    line_skip11
    dw    line_skip19

    dw    line_skip2
    dw    line_skip10
    dw    line_skip18

    dw    line_skip1
    dw    line_skip9
    dw    line_skip17

draw_a_compiled_sprite_65h
    ex    hl,de        ; hl = screen adress
    ld    de,#800-#100
    ld    bc,#xxxx    ; 2 common bytes

    ; draw line 0
    ...   

    inc    h

    ; draw line 32
    ...   

line_skip1
    add     hl,de

    ; draw line 1
    ...   

    inc    h

    ; draw line 33
    ...   

line_skip2
    add     hl,de

    ; draw line 2
    ...

    ...   

    exx
    ld    a,#19        ; #19 = add hl,de
    ld    (hl),a
    ld    (de),a
    ld    (bc),a
    jp    (ix)




Andy Coates

That some pretty clever code there. Again thanks for the input :)

Rhino

Quote from: Andy Coates on 21:31, 06 April 16
That some pretty clever code there. Again thanks for the input :)

Thanks, though I must say that currently I am working in a game where sprites are drawn approximately 40% faster than everything said here.
But still is not the time to spread all the magic than the CPC can do :)

Regards

||C|-|E||

That will be great to see!  :D

arnoldemu

@Andy Coates:

I think you should go ahead and program the cpc you can confirm your ideas and try things out. You will see what works and doesn't work and perhaps that will lead to a finished game?  :)
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

arnoldemu

Quote from: Rhino on 21:59, 06 April 16
But still is not the time to spread all the magic than the CPC can do :)

Too much magic in one go takes away all the suprises ;)

I know it'll be worth waiting for.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Andy Coates

#24
Quote from: Rhino on 21:59, 06 April 16
Thanks, though I must say that currently I am working in a game where sprites are drawn approximately 40% faster than everything said here.
But still is not the time to spread all the magic than the CPC can do :)

Regards


That's very intriguing... I suppose you don't want to share anything on this yet?


Today I was thinking about graphics and map compression. I need to investigate some good fast compression routines that are written in Z80. Then I need to investigate the compression ratio of 4096 bytes worth of graphics data (which is 256 chars). If the ratio is good then it's worth allocating a 4096byte buffer for the level graphics and use the screen area to unpack into for how many levels of compression on the data and then the final unpack to the buffer.


The map can be built from meta tile chunks that reference the chars. If the compression ratio and unpacking speed is fast then each row of the map can be unpacked into a small buffer for the scroll update to use. If it's slow then using a largish buffer will be needed to unpack the map data in. The maps should not be that big anyway because if the scroll is like Defender and wraps around on itself then you will have a continuous scroll that the player can move up and down from.

Powered by SMFPacks Menu Editor Mod