Points of Required Attention™
Please chime in on a proposed restructuring of the ROM hacking sections.
Views: 88,482,853
Main | FAQ | Uploader | IRC chat | Radio | Memberlist | Active users | Latest posts | Calendar | Stats | Online users | Search 04-25-24 06:37 PM
Guest: Register | Login

0 users currently in ROM Hacking | 2 guests | 1 bot

Main - ROM Hacking - SNES sprite rotation without FX chip New thread | New reply


Aaendi
Posted on 11-07-10 03:20 AM Link | Quote | ID: 137726


Red Koopa
Level: 27

Posts: 88/131
EXP: 109184
Next: 6975

Since: 10-18-09

Last post: 4669 days
Last view: 4330 days
This can come in handy if you want to make your SNES demo/game/hack blow everybody elses out of the water.

Use this code to software rotate sprites into ram before the level starts. It uses the PPU for data organization purposes, so it doesn't work during active display, so make sure to use a forced blanked load time before the level your using it in. If you want to use 128 kB of work ram to store the rendered sprites, you can dma it back from the PPU to the CPU side.

Sprites to use rotation need to be stored in the ROM as 256x256 8-bit bitmaps. You can store several sprites in the same bitmap, just as long as you give enough space between them so its neighbor sprites don't get rendered inside the box of rotation.

It can render up to 180 16x16 sprites per second.




!add = "clc : adc"
!sub = "sec : sbc"
!angle = "$00"
!bank = "$01"
!sine = "$02"
!cosine = "$04"
!x_pixel = "$06"
!y_pixel = "$08"
!x_displacement = "$0a"
!y_displacement = "$0c"
!width = "$0e"
!height = "$10"
!destination = "$12"
!pixel_address = "$20"
!bit_plane_1 = "$40"
!bit_plane_2 = "$41"
!bit_plane_3 = "$42"
!bit_plane_4 = "$43"



sprite_rotation:


sep #$20
lda #$84
sta $2115 ;; full graphics 4-bit v-ram loading 0aaaaaaabbbccccc -> 0aaaaaaacccccbbb
lda !bank ;; the bank that holds the 8-bit 256x256 bitmap
pha ;; of sprite being rotated
plb


rep #$30

lda !angle ;; find sine and cosine
asl
and #$01fe
tax
lda sine_table+$000000,x
sta !sine
txa
!add #$0080
and #$01fe
tax
lda sine_table+$000000,x
sta !cosine


lda !sine
!add !cosine ;; find the negative x displacement
asl #2 ;; 4 pixels up left from center
sta !x_displacement

lda !cosine ;; find the negative y displacement
!sub !sine ;; 4 pixels up left from center
asl #2
sta !y_displacement

lda !width ;; add center of sprite to x pixel coordinate
tay
asl #2
xba
!add !x_pixel
find_x_pixel:
!sub !x_displacement ;; subtract (x displacement)*width from x pixel
dey ;; coordinate
bne find_x_pixel
sta !x_pixel

lda !width ;; add center of sprite to y pixel coordinate
tay
asl #2
xba
!add !y_pixel
find_y_pixel: ;; subtract (y displacement)*width from y pixel
!sub !y_displacement ;; coordinate
dey
bne find_y_pixel
sta !y_pixel


render_sprite: ;; push x and y pixel coordinates
lda !x_pixel ;; and width onto stack to save
pha ;; for rendering the line after it
lda !y_pixel
pha
lda !width
pha
lda !destination
sta $2116
render_line:
jsr render_8_pixels
dec !width
bne render_line ;; repeat rendering 8-pixels "width" times
rep #$20
pla ;; recover x and y coordinates and width
sta !width ;; next line of pixels
pla
!add !cosine
sta !y_pixel ;; find new starting point for y coordinate
pla
!add !sine
sta !x_pixel ;; find new starting point for x coordinate
lda !destination
!add #$0020
sta !destination ;; set v-ram address reg at the next line of pixels
dec !height
bne render_sprite
rts






render_8_pixels:
rep #$30
lda !y_pixel
sta !pixel_address+2 ;; unrolled loop for finding y coordinate of 8 pixels
!sub !sine
sta !pixel_address+6
!sub !sine
sta !pixel_address+10
!sub !sine
sta !pixel_address+14
!sub !sine
sta !pixel_address+18
!sub !sine
sta !pixel_address+22
!sub !sine
sta !pixel_address+26
!sub !sine
sta !pixel_address+30
!sub !sine
sta !y_pixel
lda !x_pixel
sta !pixel_address+1 ;; unrolled loop for finding x coordinate of 8 pixels
!add !cosine ;; top byte (whole number) of x coordinate is written over
sta !pixel_address+5 ;; bottom byte (decimal) of y coordinate creating
!add !cosine ;; 16 bit pixel addresses in the form of yyyyyyyyxxxxxxxx
sta !pixel_address+9
!add !cosine
sta !pixel_address+13
!add !cosine
sta !pixel_address+17
!add !cosine
sta !pixel_address+21
!add !cosine
sta !pixel_address+25
!add !cosine
sta !pixel_address+29
!add !cosine
sta !x_pixel
sep #$20
ldx !pixel_address+2 ;; unrolled loop for fetching pixels and
lda $0000,x
ror ;; turning 8-bit packed pixel format into 4-bit planar
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+6
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+10
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+14
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+18
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+22
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+26
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+30
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !bit_plane_1
stx $2118 ;; send the 4 bytes to v-ram address port and let the PPU do the rest
ldx !bit_plane_3
stx $2118
rts




sine_table:
dw 0,6,13,19,25,31,38,44,50,56,62,68,74,80,86,92,98,104,109,115,121,126,132,137,142
dw 147,152,157,162,167,172,177,181,185,190,194,198,202,206,209,213,216,220,223,226,229
dw 231,234,237,239,241,243,245,247,248,250,251,252,253,254,255,255,256,256,256,256,256
dw 255,255,254,253,252,251,250,248,247,245,243,241,239,237,234,231,229,226,223,220,216
dw 213,209,206,202,198,194,190,185,181,177,172,167,162,157,152,147,142,137,132,126,121
dw 115,109,104,98,92,86,80,74,68,62,56,50,44,38,31,25,19,13,0,-6,-13,-19,-25,-31,-38
dw -44,-50,-56,-62,-68,-74,-80,-86,-92,-98,-104,-109,-115,-121,-126,-132,-137,-142
dw -147,-152,-157,-162,-167,-172,-177,-181,-185,-190,-194,-198,-202,-206,-209,-213,-216
dw -220,-223,-226,-229,-231,-234,-237,-239,-241,-243,-245,-247,-248,-250,-251,-252,-253
dw -254,-255,-255,-256,-256,-256,-256,-256,-255,-255,-254,-253,-252,-251,-250,-248
dw -247,-245,-243,-241,-239,-237,-234,-231,-229,-226,-223,-220,-216,-213,-209,-206,-202
dw -198,-194,-190,-185,-181,-177,-172,-167,-162,-157,-152,-147,-142,-137,-132,-126,-121
dw -115,-109,-104,-98,-92,-86,-80,-74,-68,-62,-56,-50,-44,-38,-31,-25,-19,-13,-6



blackhole89
Posted on 11-07-10 03:29 AM Link | Quote | ID: 137727


The Guardian
Moloch whose eyes are a thousand blind windows!
Level: 124

Posts: 3413/4196
EXP: 21531777
Next: 304824

Since: 02-19-07
From: Ithaca, NY, US

Last post: 470 days
Last view: 83 days



This is a pretty nice idea, but could you make a version of the code that does not depend on the PPU for the rotation? In reality, vblank time is scarce and precious more often than it is not, especially in the case of hacks where a reasonably advanced hacker (like the kind that would use your code) is setting out to perform graphics magic.

____________________



Aaendi
Posted on 11-07-10 12:50 PM Link | Quote | ID: 137733


Red Koopa
Level: 27

Posts: 89/131
EXP: 109184
Next: 6975

Since: 10-18-09

Last post: 4669 days
Last view: 4330 days
This should work without using the PPU. Just make sure you remember to set $2115 to #$84 when your dma-ing, and remember your using "4-bit full graphics" organization.




!add = "clc : adc"
!sub = "sec : sbc"
!angle = "$00"
!bank = "$01"
!sine = "$02"
!cosine = "$04"
!x_pixel = "$06"
!y_pixel = "$08"
!x_displacement = "$0a"
!y_displacement = "$0c"
!width = "$0e"
!height = "$10"
!destination = "$12"
!pixel_address = "$20"
!bit_plane_1 = "$40"
!bit_plane_2 = "$41"
!bit_plane_3 = "$42"
!bit_plane_4 = "$43"



sprite_rotation:


sep #$20
lda !bank ;; the bank that holds the 8-bit 256x256 bitmap
pha ;; of sprite being rotated
plb


rep #$30

lda !angle ;; find sine and cosine
asl
and #$01fe
tax
lda sine_table+$000000,x
sta !sine
txa
!add #$0080
and #$01fe
tax
lda sine_table+$000000,x
sta !cosine


lda !sine
!add !cosine ;; find the negative x displacement
asl #2 ;; 4 pixels up left from center
sta !x_displacement

lda !cosine ;; find the negative y displacement
!sub !sine ;; 4 pixels up left from center
asl #2
sta !y_displacement

lda !width ;; add center of sprite to x pixel coordinate
tay
asl #2
xba
!add !x_pixel
find_x_pixel:
!sub !x_displacement ;; subtract (x displacement)*width from x pixel
dey ;; coordinate
bne find_x_pixel
sta !x_pixel

lda !width ;; add center of sprite to y pixel coordinate
tay
asl #2
xba
!add !y_pixel
find_y_pixel: ;; subtract (y displacement)*width from y pixel
!sub !y_displacement ;; coordinate
dey
bne find_y_pixel
sta !y_pixel


render_sprite: ;; push x and y pixel coordinates
lda !x_pixel ;; and width onto stack to save
pha ;; for rendering the line after it
lda !y_pixel
pha
lda !width
pha
lda !destination
pha
render_line:
jsr render_8_pixels
dec !width
bne render_line ;; repeat rendering 8-pixels "width" times
pla
!add #$0040
sta !destination
pla ;; recover x and y coordinates and width
sta !width ;; next line of pixels
pla
!add !cosine
sta !y_pixel ;; find new starting point for y coordinate
pla
!add !sine
sta !x_pixel ;; find new starting point for x coordinate
dec !height
bne render_sprite
rts






render_8_pixels:
rep #$30
lda !y_pixel
sta !pixel_address+2 ;; unrolled loop for finding y coordinate of 8 pixels
!sub !sine
sta !pixel_address+6
!sub !sine
sta !pixel_address+10
!sub !sine
sta !pixel_address+14
!sub !sine
sta !pixel_address+18
!sub !sine
sta !pixel_address+22
!sub !sine
sta !pixel_address+26
!sub !sine
sta !pixel_address+30
!sub !sine
sta !y_pixel
lda !x_pixel
sta !pixel_address+1 ;; unrolled loop for finding x coordinate of 8 pixels
!add !cosine ;; top byte (whole number) of x coordinate is written over
sta !pixel_address+5 ;; bottom byte (decimal) of y coordinate creating
!add !cosine ;; 16 bit pixel addresses in the form of yyyyyyyyxxxxxxxx
sta !pixel_address+9
!add !cosine
sta !pixel_address+13
!add !cosine
sta !pixel_address+17
!add !cosine
sta !pixel_address+21
!add !cosine
sta !pixel_address+25
!add !cosine
sta !pixel_address+29
!add !cosine
sta !x_pixel
sep #$20
ldx !pixel_address+2 ;; unrolled loop for fetching pixels and
lda $0000,x
ror ;; turning 8-bit packed pixel format into 4-bit planar
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+6
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+10
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+14
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+18
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+22
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+26
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
ldx !pixel_address+30
lda $0000,x
ror
rol !bit_plane_1
ror
rol !bit_plane_2
ror
rol !bit_plane_3
ror
rol !bit_plane_4
rep #$30
ldx !destination
lda !bit_plane_1
sta $7e0000,x
lda !bit_plane_3
sta $7e0002,x
inx #4
stx !destination
rts




sine_table:
dw 0,6,13,19,25,31,38,44,50,56,62,68,74,80,86,92,98,104,109,115,121,126,132,137,142
dw 147,152,157,162,167,172,177,181,185,190,194,198,202,206,209,213,216,220,223,226,229
dw 231,234,237,239,241,243,245,247,248,250,251,252,253,254,255,255,256,256,256,256,256
dw 255,255,254,253,252,251,250,248,247,245,243,241,239,237,234,231,229,226,223,220,216
dw 213,209,206,202,198,194,190,185,181,177,172,167,162,157,152,147,142,137,132,126,121
dw 115,109,104,98,92,86,80,74,68,62,56,50,44,38,31,25,19,13,0,-6,-13,-19,-25,-31,-38
dw -44,-50,-56,-62,-68,-74,-80,-86,-92,-98,-104,-109,-115,-121,-126,-132,-137,-142
dw -147,-152,-157,-162,-167,-172,-177,-181,-185,-190,-194,-198,-202,-206,-209,-213,-216
dw -220,-223,-226,-229,-231,-234,-237,-239,-241,-243,-245,-247,-248,-250,-251,-252,-253
dw -254,-255,-255,-256,-256,-256,-256,-256,-255,-255,-254,-253,-252,-251,-250,-248
dw -247,-245,-243,-241,-239,-237,-234,-231,-229,-226,-223,-220,-216,-213,-209,-206,-202
dw -198,-194,-190,-185,-181,-177,-172,-167,-162,-157,-152,-147,-142,-137,-132,-126,-121
dw -115,-109,-104,-98,-92,-86,-80,-74,-68,-62,-56,-50,-44,-38,-31,-25,-19,-13,-6



MathOnNapkins
Posted on 11-15-10 03:51 PM Link | Quote | ID: 137923


Super Koopa
Level: 62

Posts: 823/842
EXP: 1935404
Next: 49282

Since: 02-19-07
From: durff

Last post: 4487 days
Last view: 4011 days
Nice idea but I'd probably just prerotate my sprites if I didn't have the hardware wherewithal to do it. Course, doing it at run time and buffering it in RAM would save greatly on ROM space, effectively becoming compression.

____________________
Zelda Hacking Forum
hobbies: delectatio morosa

Main - ROM Hacking - SNES sprite rotation without FX chip New thread | New reply

Acmlmboard 2.1+4δ (2023-01-15)
© 2005-2023 Acmlm, blackhole89, Xkeeper et al.

Page rendered in 0.020 seconds. (347KB of memory used)
MySQL - queries: 47, rows: 71/71, time: 0.015 seconds.