I was going to ask a question about implementing this, but the solution turned out to be much easier than I'd thought. I'm going to go ahead and post this anyway, since the concept could prove useful for some people.
My sprite drawing routine takes in 16 bit x and y values for a metasprite and calculates which parts of it to draw and which to hide. In other words, if a 32 pixel wide sprite has an x of -16, the routine will hide the left two sprites and draw the remaining part at the edge of the screen. Most people probably draw sprites this way, it's something that needs to be solved early in many NES games' development.
What I've decided to do is save on valuable RAM space by only using 8 bit x and y values for simple or unimportant objects, then convert these 8 bit values to 16 bit for the drawing routine, adjusted so that when x = 0 the entire sprite is off screen.
I like illustrating things, so here's a visual:
The idea is to have a whole screen full of clouds like this that scroll smoothly across and off the screen, but only use up two coordinate bytes instead of four. The sprite will be moving slightly faster than you tell it to, in order to account for its own width.
What you have to do is this:
sprite's 16-bit x for drawing = sprite's x - (((sprite's x eor $FF) * sprite's width) / 256)
As an example, say you have a 24 pixel wide sprite with an x value of 45. You eor with $FF to invert it, resulting in 210. 210 * 24 = 5040 / 256 = 19.6875. Subtracting 19 from the sprite's x value of 45, we find that this sprite should actually be drawn at an x of 26.
This turns out to be quite easy to do in assembly. Use a 16-bit multiplication routine, like this one I found elsewhere on this forum:
Then simply use whatever result is in the high bits as the value to subtract! With the example above, tmp16x+1 will contain the decimal value 19.
What do you guys think? Useful, not useful, potential hangups? I haven't actually implemented it yet but I like the idea.
My sprite drawing routine takes in 16 bit x and y values for a metasprite and calculates which parts of it to draw and which to hide. In other words, if a 32 pixel wide sprite has an x of -16, the routine will hide the left two sprites and draw the remaining part at the edge of the screen. Most people probably draw sprites this way, it's something that needs to be solved early in many NES games' development.
What I've decided to do is save on valuable RAM space by only using 8 bit x and y values for simple or unimportant objects, then convert these 8 bit values to 16 bit for the drawing routine, adjusted so that when x = 0 the entire sprite is off screen.
I like illustrating things, so here's a visual:
The idea is to have a whole screen full of clouds like this that scroll smoothly across and off the screen, but only use up two coordinate bytes instead of four. The sprite will be moving slightly faster than you tell it to, in order to account for its own width.
What you have to do is this:
sprite's 16-bit x for drawing = sprite's x - (((sprite's x eor $FF) * sprite's width) / 256)
As an example, say you have a 24 pixel wide sprite with an x value of 45. You eor with $FF to invert it, resulting in 210. 210 * 24 = 5040 / 256 = 19.6875. Subtracting 19 from the sprite's x value of 45, we find that this sprite should actually be drawn at an x of 26.
This turns out to be quite easy to do in assembly. Use a 16-bit multiplication routine, like this one I found elsewhere on this forum:
Code:
; routine originally created by frantik
; val1 = first 8-bit number to multiply
; val2 = second 8-bit number to multiply (cannot be zero)
; tmp16x = 16-bit result (output)
; temp = temporary variable
; x and y registers are preserved
mult16
lda #$00 ; clear temporary variables
sta tmp16x
sta tmp16x+1
sta temp
jmp multstart
-loop
asl val1 ; double first value
rol temp ; using 16bit precision
lsr val2 ; halve second value
multstart
lda val2
and #01 ; is new 2nd value an odd number?
beq -loop
clc ; if so, add new 1st value to running total
lda tmp16x
adc val1
sta tmp16x
lda tmp16x+1
adc temp
sta tmp16x+1
lda val2
cmp #01 ; is 2nd value 1? if so, we're done
bne -loop ; otherwise, loop
rts
; val1 = first 8-bit number to multiply
; val2 = second 8-bit number to multiply (cannot be zero)
; tmp16x = 16-bit result (output)
; temp = temporary variable
; x and y registers are preserved
mult16
lda #$00 ; clear temporary variables
sta tmp16x
sta tmp16x+1
sta temp
jmp multstart
-loop
asl val1 ; double first value
rol temp ; using 16bit precision
lsr val2 ; halve second value
multstart
lda val2
and #01 ; is new 2nd value an odd number?
beq -loop
clc ; if so, add new 1st value to running total
lda tmp16x
adc val1
sta tmp16x
lda tmp16x+1
adc temp
sta tmp16x+1
lda val2
cmp #01 ; is 2nd value 1? if so, we're done
bne -loop ; otherwise, loop
rts
Then simply use whatever result is in the high bits as the value to subtract! With the example above, tmp16x+1 will contain the decimal value 19.
What do you guys think? Useful, not useful, potential hangups? I haven't actually implemented it yet but I like the idea.