Welcome, Guest. Please login or register.


Author Topic: Sprite flipping and shifting methods (Read 1235 times)

Offline ssr86

  • EP user
  • *
  • Posts: 359
  • Country: pl
  • OS:
  • Windows NT 6.2 Windows NT 6.2
  • Browser:
  • Firefox 42.0 Firefox 42.0
    • View Profile
Sprite flipping and shifting methods
« on: 2015.November.10. 22:43:01 »
Here's a code example for flipping a standard sprite "in place".
You would need to call this only when the sprite changes it's facing direction.
This method doesn't need an additional buffer.
The sprite data is stored normally.

It should be good in cases when the sprite seldom changes the direction it is facing.
Maybe when the direction of the sprite's standing pose changes only after a jump then the sprite data for the standing animation could be reversed during the jumping animation...

The code is written for sprites with even width but can be modified to work with odd widths too...
Here there's a byte pair in the middle and for the odd version there would be a single byte to reverse.
The values to add to hl and de to get to the next line of the sprite data would slightly change too.

The lookup table at the end is for 16-color mode pixels.

Code: [Select]
flip_sprite:
                     ld b,reverse_table/256                                          ;; bc is used for accessing the reversed byte values
                     ld de,sprite_data+sprite_width_in_bytes-1                       ;; de = address of the end of the first sprite data line
                     ld hl,sprite_data                                               ;; hl = address of the sprite data

                     exx
                     ld b,sprite_height
flip_line_loop:
                     exx

                     ld a,(de)          ;; get left byte
                     ld c,a             ;;
                     ld a,(bc)          ;; reverse left byte
                     ex af,af'
                     ld c,(hl)          ;; get right byte
                     ld a,(bc)          ;; reverse it
                     ld (de),a          ;; store it on the left side of the sprite data line
                     ex af,af'
                     ld (hl),a          ;; store the left byte on the right side of the sprite data line
                    
                     dec e              ;; here I assume that the sprite data is aligned (not necessarily page aligned)
                     inc l              ;;

                     ;; repeat the part above k = (sprite_width_in_bytes / 2 - 1 ) times
[...]
                     ;; for the last pair of bytes in the line loop we can drop the dec/inc part:

                     ld a,(de)
                     ld c,a
                     ld a,(bc)
                     ex af,af'
                     ld c,(hl)
                     ld a,(bc)
                     ld (de),a
                     ex af,af'
                     ld (hl),a        

                     ;; get to the next sprite data line

                     ld a,l
                     add a,sprite_width_in_bytes/2+1
                     ld l,a
                     adc a,h
                     sub l
                     ld h,a

                     ld a,e
                     add a,3*sprite_width_in_bytes/2-1
                     ld e,a
                     adc a,d
                     sub e
                     ld d,a

                     exx
                     djnz  flip_line_loop

                     exx
                     ret

org ($/256+1)*256
reverse_table:
db $00,$02,$01,$03,$08,$0A,$09,$0B,$04,$06,$05,$07,$0C,$0E,$0D,$0F
db $20,$22,$21,$23,$28,$2A,$29,$2B,$24,$26,$25,$27,$2C,$2E,$2D,$2F
db $10,$12,$11,$13,$18,$1A,$19,$1B,$14,$16,$15,$17,$1C,$1E,$1D,$1F
db $30,$32,$31,$33,$38,$3A,$39,$3B,$34,$36,$35,$37,$3C,$3E,$3D,$3F
db $80,$82,$81,$83,$88,$8A,$89,$8B,$84,$86,$85,$87,$8C,$8E,$8D,$8F
db $A0,$A2,$A1,$A3,$A8,$AA,$A9,$AB,$A4,$A6,$A5,$A7,$AC,$AE,$AD,$AF
db $90,$92,$91,$93,$98,$9A,$99,$9B,$94,$96,$95,$97,$9C,$9E,$9D,$9F
db $B0,$B2,$B1,$B3,$B8,$BA,$B9,$BB,$B4,$B6,$B5,$B7,$BC,$BE,$BD,$BF
db $40,$42,$41,$43,$48,$4A,$49,$4B,$44,$46,$45,$47,$4C,$4E,$4D,$4F
db $60,$62,$61,$63,$68,$6A,$69,$6B,$64,$66,$65,$67,$6C,$6E,$6D,$6F
db $50,$52,$51,$53,$58,$5A,$59,$5B,$54,$56,$55,$57,$5C,$5E,$5D,$5F
db $70,$72,$71,$73,$78,$7A,$79,$7B,$74,$76,$75,$77,$7C,$7E,$7D,$7F
db $C0,$C2,$C1,$C3,$C8,$CA,$C9,$CB,$C4,$C6,$C5,$C7,$CC,$CE,$CD,$CF
db $E0,$E2,$E1,$E3,$E8,$EA,$E9,$EB,$E4,$E6,$E5,$E7,$EC,$EE,$ED,$EF
db $D0,$D2,$D1,$D3,$D8,$DA,$D9,$DB,$D4,$D6,$D5,$D7,$DC,$DE,$DD,$DF
db $F0,$F2,$F1,$F3,$F8,$FA,$F9,$FB,$F4,$F6,$F5,$F7,$FC,$FE,$FD,$FF

Other methods will follow as I have to adapt the examples for the Enterprise...

EDIT: added a working example...
« Last Edit: 2015.November.13. 22:27:30 by ssr86 »

Offline ssr86

  • EP user
  • *
  • Posts: 359
  • Country: pl
  • OS:
  • Windows NT 6.2 Windows NT 6.2
  • Browser:
  • Firefox 42.0 Firefox 42.0
    • View Profile
Re: Sprite flipping and shifting methods
« Reply #1 on: 2015.November.13. 22:32:10 »
"In-place" shifting:

Here we need two routines - left shift and right shift. That's because we change our sprite data permanently so for the "normal" frame we have to shift the sprite back.
Code: [Select]
; SHIFT SPRITE LEFT (changing sprite data)
; using DE as $aa55 (pixel mask) for optimization
shift_sprite_left:

ld hl,hero_normal+sprite_height*sprite_width_in_bytes-1
ld de,$aa55
ld iyl,sprite_height

lshift_height_loop:

;rightmost byte
ld a,(hl)           ; get byte
and d             ; get left pixel
rrca
ld c,a             ; save it for later
ld a,(hl)           ; get the same byte again
and e           ; get right pixel
rlca               ; shift it to left position
ld (hl),a           ; save byte
dec hl             ; go to previous byte

lshift_2byte macro
ld a,(hl)           ; get byte
and d           ; get left pixel
rrca             ; shift to right pixel position
ld b,a             ; save for later
ld a,(hl)           ; get the same byte again
and e             ; get right pixel
rlca             ; shift to left pixel position
or c               ; or with shifted right pixel from previous byte
ld (hl),a           ; save byte
dec hl             ; go to previous byte

ld a,(hl)           ; get byte
and d             ; get left pixel
rrca               ; shift to right pixel position
ld c,a             ; save for later
ld a,(hl)           ; get the same byte again
and e           ; get right pixel
rlca               ; shift to left pixel position
or b               ; or with shifted right pixel from previous byte
ld (hl),a           ; save byte
dec hl             ; go to previous byte
endm

rept sprite_width_in_bytes/2-1
lshift_2byte
endm

ld a,(hl)           ; get the same byte again
and e           ; get right pixel
rlca               ; shift to left pixel position
or c               ; or with shifted right pixel from previous byte
ld (hl),a           ; save byte
dec hl             ; go to previous byte

dec iyl
jp nz,lshift_height_loop

ret

; SHIFT SPRITE RIGHT (changing sprite data)
; using de as $aa55 (pixel mask) for optimization
shift_sprite_right:

ld hl,hero_normal
ld de,$aa55
ld iyl,sprite_height

rshift_height_loop:

;leftmost byte
ld a,(hl)           ; get byte
and e             ; get right pixel
rlca
ld c,a             ; save it for later
ld a,(hl)           ; get the same byte again
and d           ; get left pixel
rrca               ; shift it to right position
ld (hl),a           ; save byte
inc hl             ; go to next byte

rshift_2byte macro
ld a,(hl)           ; get byte
and e           ; get right pixel
rlca             ; shift to left pixel position
ld b,a             ; save for later
ld a,(hl)           ; get the same byte again
and d             ; get left pixel
rrca             ; shift to right pixel position
or c               ; or with shifted right pixel from previous byte
ld (hl),a           ; save byte
inc hl             ; go to next byte

ld a,(hl)           ; get byte
and e             ; get right pixel
rlca               ; shift to left pixel position
ld c,a             ; save for later
ld a,(hl)           ; get the same byte again
and d             ; get left pixel
rrca               ; shift to right pixel position
or b               ; or with shifted right pixel from previous byte
ld (hl),a           ; save byte
inc hl             ; go to next byte
endm

rept sprite_width_in_bytes/2-1
rshift_2byte
endm

ld a,(hl)           ; get the same byte again
and d             ; get left pixel
rrca               ; shift to right pixel position
or c               ; or with shifted right pixel from previous byte
ld (hl),a           ; save byte
inc hl             ; go to next byte

dec iyl
jp nz,rshift_height_loop

ret

I have attached a working example...

I think it could be done better and be faster with using some LUT... but haven't tried yet...
« Last Edit: 2015.November.13. 22:37:16 by ssr86 »

Offline Z80System

  • EP addict
  • *
  • Posts: 3926
  • Country: hu
  • OS:
  • Windows 7/Server 2008 R2 Windows 7/Server 2008 R2
  • Browser:
  • Chrome 46.0.2490.86 Chrome 46.0.2490.86
    • View Profile
Re: Sprite flipping and shifting methods
« Reply #2 on: 2015.November.13. 23:18:35 »
Do you ssr86 write such an amount of comments for the assembly sources like these,

or is it only for the explanation for the web ?
« Last Edit: 2015.November.13. 23:46:04 by Z80System »
Z80 System

Offline ssr86

  • EP user
  • *
  • Posts: 359
  • Country: pl
  • OS:
  • Windows NT 6.2 Windows NT 6.2
  • Browser:
  • Firefox 42.0 Firefox 42.0
    • View Profile
Re: Sprite flipping and shifting methods
« Reply #3 on: 2015.November.13. 23:42:47 »
Do you ssr86 write such an amount of comments for the assembly source like these,

or is it only for the explanation for the web ?
I guess, too much...
The comments are only for explanation purposes but as I read it now there're many unnecessary ones...

Well the examples are nearly a year old.. I've wrote them to compare different methods that I could come up with... I've published most of it on the cpcwiki many months ago but lost the motivation to finish it off (took too much time and I haven't met much interest on the subject on that forum, well I don't expect more from ef ;P, but wanted to post what I have here too).
« Last Edit: 2015.November.13. 23:52:07 by ssr86 »

Offline ssr86

  • EP user
  • *
  • Posts: 359
  • Country: pl
  • OS:
  • Windows NT 6.2 Windows NT 6.2
  • Browser:
  • Firefox 42.0 Firefox 42.0
    • View Profile
Re: Sprite flipping and shifting methods
« Reply #4 on: 2015.November.14. 00:01:27 »
Vertical flip with the "in-place" method:
Code: [Select]
flip_sprite_vertical:
; INPUT:
; DE = sprite data

ld de,hero_normal

; make hl point at the start of the last line of sprite data
ld hl,sprite_width_in_bytes * (sprite_height-1)
add hl,de                               

ld iyh,sprite_height/2

vflip_loop:

rept sprite_width_in_bytes - 1

         ld a,(de)
         ld c,a
         ld a,(hl)
         ld (hl),c
         ld (de),a

inc de
inc hl

endm

ld a,(de)
ld c,a
ld a,(hl)
ld (hl),c
ld (de),a

inc de
;inc hl

ld bc,-2*sprite_width_in_bytes-1
add hl,bc

dec iyh
jp nz, vflip_loop

ret

Offline Z80System

  • EP addict
  • *
  • Posts: 3926
  • Country: hu
  • OS:
  • Windows 7/Server 2008 R2 Windows 7/Server 2008 R2
  • Browser:
  • Chrome 46.0.2490.86 Chrome 46.0.2490.86
    • View Profile
Re: Sprite flipping and shifting methods
« Reply #5 on: 2015.November.14. 00:16:29 »
Quote
I guess, too much...

Not "too much" for the reading, but the writing ...

In Hungary there was a book called "The explanation of the ROM of the Enterprise 128" or "Reverse engineering of the Enterprise 128's ROM":
http://ep128.hu/Ep_Konyv/Pic/Ep_ROM.jpg

and this book was (a big A4 based book with a width like a Harry Potter book's) a printing of the EP128's ROM adding comments line by line, like in your examples ...

They commented that, instruction by instruction ... :)


So ... reading is good, but the commenting is slow enough ... I just wondered you always code like this, or it's an exception ...
Z80 System

Offline ssr86

  • EP user
  • *
  • Posts: 359
  • Country: pl
  • OS:
  • Windows NT 6.2 Windows NT 6.2
  • Browser:
  • Firefox 42.0 Firefox 42.0
    • View Profile
Re: Sprite flipping and shifting methods
« Reply #6 on: 2015.November.14. 16:14:19 »
So ... reading is good, but the commenting is slow enough ... I just wondered you always code like this, or it's an exception ...
Well it seems that when doing the examples I got "bored" of adding that many comments pretty quick, because the other ones seem to have much less of that;P

However I would like my code to be commented in a good way, because doing so you can come back to such project more quickly, also you can publish your code and people can make use of it loosing much less time figuring out what the author is doing and why...
...But my code is rather messy although much still much better then some time before;P

Offline ssr86

  • EP user
  • *
  • Posts: 359
  • Country: pl
  • OS:
  • Windows NT 6.2 Windows NT 6.2
  • Browser:
  • Firefox 42.0 Firefox 42.0
    • View Profile
Re: Sprite flipping and shifting methods
« Reply #7 on: 2015.November.14. 21:12:11 »
Vertical flip using a temporary buffer for storing the flipped sprite... Although I think this would be useful only as means of "decompression" of the flipped sprite frames...

Two versions - the first uses ldi's for copying and the second uses stack.

The routines are just copying the sprite line by line but in inverted order...

Code: [Select]
flip_sprite_vertical:
; INPUT:
; HL = sprite data

; make HL point at the start of the last line of the sprite
ld bc,sprite_width_in_bytes*(sprite_height-1)
add hl,bc

ld de,flip_buffer

ld a,sprite_height

fsvloop:

rept sprite_width_in_bytes
        ldi
endm

        ld bc,-2*sprite_width_in_bytes
        add hl,bc

        dec a
        jp nz, fsvloop

ret

flip_buffer ds sprite_height*sprite_width_in_bytes,0

Code: [Select]
flip_sprite_vertical:
; INPUT:
; DE = sprite data
       
        di
; make HL point at the start of the last line of the sprite
ld hl,sprite_width_in_bytes * (sprite_height-1)
add hl,de                               

ld (save_sp),sp

ld de, flip_buffer + sprite_width_in_bytes

ld a,sprite_height
fsvloop:
ld i,a
; de = hero_frame + sprite_width_in_bytes * i
; hl = hero_frame0 + sprite_width_in_bytes * (sprite_height-1)
; to buffer...
ld sp,hl ; pop data from sprite frame buffer

pop af
ex af,af'

pop af
pop bc
exx
pop bc
pop de
pop hl
;pop ix
;pop iy

exx
ex de,hl
ld sp,hl ; push data to flip_buffer
ex de,hl
exx

;push iy
;push ix
push hl
push de
push bc
exx
push bc
push af
ex af,af'
push af

ld bc,sprite_width_in_bytes
ex de,hl
add hl,bc    ;modify pointer to flip_buffer
ex de,hl
ld bc,-sprite_width_in_bytes
add hl,bc    ;modify data pointer


ld a,i
dec a
jp nz,fsvloop

fvend:
save_sp        equ $+1
ld sp,0000

ei
ret

flip_buffer ds sprite_height*sprite_width_in_bytes,0