More work on sprite math in 6502.

This commit is contained in:
Martin Haye 2014-05-08 10:41:15 -07:00
parent 53318fc2cd
commit 9f7d551fd0
2 changed files with 296 additions and 42 deletions

View File

@ -64,7 +64,7 @@ var options = 0;
var debugRay = null; /* Debugging info printed about this ray num, or null for none */
var debugSprite = 3; /* Debugging info printed about this sprite, or null for none */
var debugSprite = 0; /* Debugging info printed about this sprite, or null for none */
var maxAngleNum = 16;
@ -110,6 +110,7 @@ var overlayText = "";
// Constants
var wLog256;
var wLog128;
var wLogViewDist;
// Tables
@ -261,6 +262,7 @@ function initCast()
// Sprite math constants
wLog256 = log2_w_w(256);
wLog128 = log2_w_w(128);
wLogViewDist = log2_w_w(viewDist/8*256); // div by 8 to get to Apple II coords
// Sine table
@ -354,6 +356,7 @@ function printPrecast() {
console.log("");
console.log("wLog256: .word " + wordToHex(wLog256));
console.log("wLog128: .word " + wordToHex(wLog128));
console.log("wLogViewDist: .word " + wordToHex(wLogViewDist));
console.log("");
@ -383,14 +386,20 @@ function calcZ(wLogHeight) {
return ubyte(wLogHeight>>4);
}
/**
* This routine uses integer math to perform the calculations originally
* developed in floatRenderSprites(). To understand the math, you might
* want to start by reading the float version first, then this, and then
* finally the 6502 code that does these integer calculations.
*/
function intRenderSprites()
{
// Quantities that are the same for every sprite
var bSgnSinT = (tbl_wLogSin[player.angleNum] & 0x8000) ? -1 : 1;
var wLogSinT = (tbl_wLogSin[player.angleNum] & 0x7FFF);
var wLogSinT = (tbl_wLogSin[player.angleNum] & 0x7FFF) - wLog256;
var cosAngle = (player.angleNum - 4) & 15;
var bSgnCosT = (tbl_wLogSin[cosAngle] & 0x8000) ? -1 : 1;
var wLogCosT = (tbl_wLogSin[cosAngle] & 0x7FFF);
var wLogCosT = (tbl_wLogSin[cosAngle] & 0x7FFF) - wLog256;
// Now process each sprite
for (var i=0;i<allSprites.length;i++) {
@ -411,8 +420,8 @@ function intRenderSprites()
var bSgnDy = (dy < 0) ? -1 : 1;
var wLogDy = log2_w_w(uword(Math.abs(dy)*256));
var wRx = bSgnDx*bSgnCosT*pow2_w_w(wLogDx + wLogCosT - wLog256) -
bSgnDy*bSgnSinT*pow2_w_w(wLogDy + wLogSinT - wLog256);
var wRx = bSgnDx*bSgnCosT*pow2_w_w(wLogDx + wLogCosT) -
bSgnDy*bSgnSinT*pow2_w_w(wLogDy + wLogSinT);
// If sprite is behind the viewer, skip it.
if (wRx < 0) {
@ -423,55 +432,60 @@ function intRenderSprites()
continue;
}
var wRy = bSgnDx*bSgnSinT*pow2_w_w(wLogDx + wLogSinT - wLog256) +
bSgnDy*bSgnCosT*pow2_w_w(wLogDy + wLogCosT - wLog256);
var wRy = bSgnDx*bSgnSinT*pow2_w_w(wLogDx + wLogSinT) +
bSgnDy*bSgnCosT*pow2_w_w(wLogDy + wLogCosT);
// Transform wRy to abs and sign
var bSgnRy = 1;
if (wRy < 0) {
bSgnRy = -1;
wRy = -wRy;
}
// Calculate the distance
var wLogSqRx = Math.max(0, (log2_w_w(Math.abs(wRx)) << 1) - wLog256);
var wLogSqRy = Math.max(0, (log2_w_w(Math.abs(wRy)) << 1) - wLog256);
var wLogSqRx = (log2_w_w(wRx) << 1) - wLog256;
var wLogSqRy = (log2_w_w(wRy) << 1) - wLog256;
var wSqDist = pow2_w_w(wLogSqRx) + pow2_w_w(wLogSqRy);
var wLogDist = ((log2_w_w(wSqDist)-wLog256) >> 1) + wLog256;
var wLogDist = (log2_w_w(wSqDist) + wLog256) >> 1;
// size of the sprite
var wLogSize = wLogViewDist - wLogDist;
var wSize = pow2_w_w(wLogSize);
// x-position on screen
var bSgnRy = wRy < 0 ? -1 : 1;
// The constant below is cheesy and based on empirical observation rather than understanding.
// Sorry :/
var wX = bSgnRy * pow2_w_w(log2_w_w(Math.abs(wRy)) - wLogDist + log2_w_w(252 / 8 / 0.44));
var wX = bSgnRy * pow2_w_w(log2_w_w(wRy) - wLogDist + log2_w_w(252 / 8 / 0.44));
if (sprite.index == debugSprite)
console.log(" wRx/256=" + (wRx/256.0) + ", wRy/256=" + (wRy/256.0) + ", wSize=" + wSize + ", wX=" + wX);
// If no pixels on screen, skip it
var spriteLeft = (screenWidth/8/2) + wX - (wSize/2);
var spriteRight = spriteLeft + wSize;
var spriteTop = ((screenHeight/8) - wSize) / 2;
if (spriteRight < 0) {
var wSpriteTop = 32 - (wSize >> 1);
var wSpriteLeft = wX + wSpriteTop;
if (wSpriteLeft < -wSize) {
if (sprite.index == debugSprite)
console.log(" off-screen to left.");
console.log(" off-screen to left (wSpriteLeft=" + wSpriteLeft + ", -wSize=" + (-wSize) + ").");
sprite.visible = false;
sprite.img.style.display = "none";
continue;
}
else if (spriteLeft > (screenWidth/8)) {
else if (wSpriteLeft > 63) {
if (sprite.index == debugSprite)
console.log(" off-screen to right.");
console.log(" off-screen to right (wSpriteLeft=" + wSpriteLeft + ", vs 63).");
sprite.visible = false;
sprite.img.style.display = "none";
continue;
}
// Adjust from Apple II coordinates to PC coords (we render 8 pixels for each 1 Apple pix)
spriteLeft *= 8;
spriteTop *= 8;
wSpriteLeft *= 8;
wSpriteTop *= 8;
wSize *= 8;
// Update the image with the calculated values
sprite.visible = true;
img.style.left = spriteLeft + "px";
img.style.top = spriteTop+"px";
img.style.left = wSpriteLeft + "px";
img.style.top = wSpriteTop+"px";
img.style.width = wSize + "px";
img.style.height = wSize + "px";
// The constant below is cheesy and I'm not sure why it's needed. But it seems to

View File

@ -28,6 +28,10 @@ DEBUG = 1 ; 1=some logging, 2=lots of logging
MAX_SPRITES = 16 ; max # sprites visible at once
NUM_COLS = 63
; Useful constants
W_LOG_256 = $0800
W_LOG_VIEW_DIST = $0E3F
; Variables
backBuf: !byte 0 ; (value 0 or 1)
frontBuf: !byte 0 ; (value 0 or 1)
@ -575,26 +579,266 @@ spriteFu:
;------------------------------------------------------------------------------
spriteCalc: !zone
ldy #0
lda spriteX
lda #0 ; track sign bits
sta .bSgnSinT
sta .bSgnCosT
sta .bSgnDx
sta .bSgnDy
sta .bSgnRy
; Look up sin of player direction, minus wLog256, and store it
lda playerDir
asl
tay
lda sinTbl,y
sta .wLogSinT ; store lo byte
lda sinTbl+1,y
bpl + ; sign bit clear?
and #$7F ; sign bit was set - mask it off
inc .bSgnSinT ; and update sign flag
+ sec
sbc #8 ; subtract wLog256
sta .wLogSinT+1 ; store hi byte
; Look up cos of player direction, minus wLog256, and store it
lda playerDir
sec
sbc #4 ; cos(a) = sin(a - 90 degrees)
and #$F ; wrap around
asl
tay
lda sinTbl,y
sta .wLogCosT ; store lo byte
lda sinTbl+1,y
bpl + ; sign bit clear?
and #$7F ; sign bit was set - mask it off
inc .bSgnCosT ; and update sign byte
+ sec
sbc #8 ; subtract wLog256
sta .wLogCosT+1 ; store hi byte
; Calculate wDx = spriteX - playerX, as abs value and a sign bit
lda spriteX ; calculate spriteX - playerX
sec
sbc playerX
pha
lda spriteX+1
tay ; stash lo byte
lda spriteX+1 ; work on hi byte
sbc playerX+1
bcs +
eor #$FF
tax ; put hi byte in X where we need it
bpl + ; if positive, no inversion necessary
inc .bSgnDx ; flip sign bit for output
jsr .negYX ; negate to get absolute value
+ tya ; lo byte in A where log2 wants it
jsr log2_w_w ; wants A=lo, X=Hi
sta .wLogDx
stx .wLogDx+1
; Calculate wDy = spriteY - playerY, as abs value and a sign bit
lda spriteY ; calculate spriteX - playerX
sec
sbc playerY
tay ; stash lo byte
lda spriteY+1 ; work on hi byte
sbc playerY+1
tax ; put hi byte in X where we need it
bpl + ; if positive, no inversion necessary
inc .bSgnDy ; flip sign bit for output
jsr .negYX ; negate to get absolute value
+ tya ; lo byte in A where log2 wants it
jsr log2_w_w ; wants A=lo, X=Hi
sta .wLogDy
stx .wLogDy+1
; Calculate wRx = bSgnDx*bSgnCosT*pow2_w_w(wLogDx + wLogCosT) -
; bSgnDy*bSgnSinT*pow2_w_w(wLogDy + wLogSinT)
lda .wLogDx ; start with lo byte
clc
adc .wLogCosT
tay ; put it in Y where pow2 wants it
lda .wLogDx+1 ; now do hi byte
adc .wLogCosT+1
tax ; in X where pow2 wants it
jsr pow2_w_w ; transform from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
tay ; set lo byte aside
lda .bSgnDx
eor .bSgnCosT ; multiply the two sign bits together
beq + ; if result is clear, no negation
jsr .negYX ; negate
+ sty .wRx ; save partial result
stx .wRx+1
lda .wLogDy ; start with lo byte
clc
adc .wLogSinT
tay ; put it in Y where pow2 wants it
lda .wLogDy+1 ; now do hi byte
adc .wLogSinT+1
tax ; in X where pow2 wants it
jsr pow2_w_w ; transform from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
tay ; set lo byte aside
lda .bSgnDy
eor .bSgnSinT ; multiply the two sign bits together
eor #1 ; one extra inversion since we want to end up subtracting this
beq + ; if result is clear, no negation
jsr .negYX ; negate
+ tya
clc
adc .wRx ; add to partial result
sta .wRx
txa
adc .wRx+1 ; also hi byte
sta .wRx+1
; if wRx is negative, it means sprite is behind viewer... we get out of school early.
bpl +
!if DEBUG { +prStr : !text "Sprite is behind viewer.",0 }
rts
; Calculate wRy = bSgnDx*bSgnSinT*pow2_w_w(wLogDx + wLogSinT) +
; bSgnDy*bSgnCosT*pow2_w_w(wLogDy + wLogCosT);
+ lda .wLogDx ; start with lo byte
clc
adc .wLogSinT
tay ; put it in Y where pow2 wants it
lda .wLogDx+1 ; now do hi byte
adc .wLogSinT+1
tax ; in X where pow2 wants it
jsr pow2_w_w ; transform from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
tay ; set lo byte aside
lda .bSgnDx
eor .bSgnSinT ; multiply the two sign bits together
beq + ; if result is clear, no negation
jsr .negYX ; negate
+ sty .wRy ; save partial result
stx .wRy+1
lda .wLogDy ; start with lo byte
clc
adc .wLogCosT
tay ; put it in Y where pow2 wants it
lda .wLogDy+1 ; now do hi byte
adc .wLogCosT+1
tax ; in X where pow2 wants it
jsr pow2_w_w ; transform from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
tay ; set lo byte aside
lda .bSgnDy
eor .bSgnCosT ; multiply the two sign bits together
beq + ; if result is clear, no negation
jsr .negYX ; negate
+ tya
clc
adc .wRy ; add to partial result
tay
txa
adc .wRy+1 ; also hi byte
tax
pla
eor #$FF
adc #1
bcc ++
bpl + ; if already positive, skip negation
jsr .negYX ; negate to get abs value
inc .bSgnRy ; and update sign bit
+ sty .wRy ; save result (we may not actually need to do this, but it helps w/ debug)
stx .wRy+1
tya ; get lo byte where it needs to be for log2
jsr log2_w_w ; calculate the log of wRy
sta .wLogRy ; save it for later
stx .wLogRy+1
; Calculate wLogSqRy = (log2_w_w(wRy) << 1) - wLog256;
asl ; we already have it in register. Shift it up 1 bit
sta .wLogSqRy ; save lo byte
txa ; get hi byte
rol ; shift up 1 bit, with carry from lo byte
sec
sbc #8 ; subtract wLog256 = $800
sta .wLogSqRy+1
; Calculate wLogSqRx = (log2_w_w(wRx) << 1) - wLog256
+ lda .wRx
ldx .wRx+1
jsr log2_w_w ; calculate log of wRx
asl ; shift it up 1 bit
sta .wLogSqRx ; save lo byte
tay ; save it also in Y for upcoming pow2
txa ; get hi byte
rol ; shift up 1 bit, with carry from lo byte
sec
sbc #8 ; subtract wlog256 = $800
sta .wLogSqRx+1
; Calculate wSqDist = pow2_w_w(wLogSqRx) + pow2_w_w(wLogSqRy)
tax ; get lo byte where we need for pow2 (hi byte already in Y)
jsr pow2_w_w ; convert back to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
sta .wSqDist ; save partial result
stx .wSqDist+1
ldy .wLogSqRy ; get wLogSqRy into the right regs
ldx .wLogSqRy+1
jsr pow2_w_w ; convert it back to normal space also (in: Y=lo,X=hi, out: A=lo,X=hi)
clc
adc .wSqDist ; add to previous partial result (lo byte)
sta .wSqDist ; save lo byte
tay ; also stash aside
txa
adc .wSqDist+1 ; hi byte of partial
sta .wSqDist+1 ; save hi byte
; Calculate wLogDist = (log2_w_w(wSqDist) + wLog256) >> 1
txa ; hi byte in X
tya ; lo byte in A
jsr log2_w_w ; convert to log space
tay ; set aside lo byte
txa ; work on hi byte
clc
adc #8 ; add wLog256 = $800
lsr ; shift right 1 bit -> carry
sta .wLogDist+1
tya ; finish off lo byte
ror ; shift right with carry from hi byte
sta .wLogDist
; Calculate wSize = pow2_w_w(wLogViewDist - wLogDist)
lda #<W_LOG_VIEW_DIST ; lo byte of constant
sec
sbc wLogDist ; minus log dist
tay ; lo byte where pow2 wants it
lda #>W_LOG_VIEW_DIST ; hi byte of constant
sbc wLogDist ; minus log dist
tax ; hi byte where pow2 wants it
jsr pow2_w_w ; get back from log space to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
sta .wSize
stx .wSize+1
; Calculate wX = bSgnRy * pow2_w_w(log2_w_w(wRy) - wLogDist + log2_w_w(252 / 8 / 0.44))
; Note: log2_w_w(252 / 8 / 0.44) = $626
lda .wLogRy ; calc wRy minus wLogDist, lo byte
sec
sbc .wLogDist
tay ; stash lo byte temporarily
lda .wLogRy+1 ; now work on hi byte
sbc .wLogDist+1
tax ; stash hi byte
tya ; back to lo byte
clc
adc #$26 ; add lo byte of const log2_w_w(252 / 8 / 0.44)
tay ; put it where pow2 wants it
txa ; finish off hi byte
adc #6 ; hi byte of const
tax ; put it where pow2 wants it
jsr pow2_w_w ; back to normal space (in: Y=lo,X=hi, out: A=lo,X=hi)
sta .wX
stx .wX+1
; TODO: finish this routine
.negYX: ; subroutine to negate value in Y=lo,X=hi.
tya
eor #$FF ; invert lo byte
tay
txa
eor #$FF ; invert hi byte
tax
iny ; bump by 1 for true negation
bne +
inx
+ pla
++ sty .bSgn1
jsr log2_w_w
sta .wSum1
stx .wSum1+1
+ rts
;------------------------------------------------------------------------------
; Save a link in the linked column data, sorted according to its depth.
@ -2750,10 +2994,6 @@ firstLink: !fill NUM_COLS
mapSpriteL !fill MAX_SPRITES
mapSpriteH !fill MAX_SPRITES
; Useful constants
wLog256: !word $0800
wLogViewDist: !word $0E3F
; Movement amounts when walking at each angle
; Each entry consists of an X bump and a Y bump, in 8.8 fixed point
walkDirs: