196
CaDan SHMUP / Re: Yet another shooter
« on: October 22, 2011, 06:23:04 am »
Character roster is incomplete, so I can't post that.
Verified that sprite rendering and clipping works flawlessly. Since I'm not using "normal" math to calculate some of the addresses, I find it funny that due to that fact, I had to deal with a literal corner case. The top-left corner to be exact. When you try to left-clip up there, the resulting number is negative, and that didn't sit well with the cheap as hell "addition" of $8000 by simply setting bit 7 of HL to find the buffer address.
Here's the routine that's in use (nevermind some of the sillier comments) :
Sprite rendering is done during the stage script, purely for testing purposes. This is the code I've used (the whole thing, including the lines going across the screen; relevant code near bottom) :
And attached to the end of the post is a screenshot of the actual test.
Verified that sprite rendering and clipping works flawlessly. Since I'm not using "normal" math to calculate some of the addresses, I find it funny that due to that fact, I had to deal with a literal corner case. The top-left corner to be exact. When you try to left-clip up there, the resulting number is negative, and that didn't sit well with the cheap as hell "addition" of $8000 by simply setting bit 7 of HL to find the buffer address.
Here's the routine that's in use (nevermind some of the sillier comments) :
Code: [Select]
;Automagically clips x bounds. Idea given by calc84maniac
DrawClippedSprite: ;D=X E=Y A=spriteID. Allows negative numbers to an extent.
;screen dimensions are 64x64 pixels
rrca
rrca ;%11000000 get
ld c,a ;A is saved into C. Now let's start doing display sanity checks.
ld hl,$F940 ;H=-7 (carry=oob), L=64 (nocarry=oob)
ld a,d
cp L
jr c,_ ;skip further checking. We are in bounds
cp H
ret c ;kill routine if the sprite can't be displayed.
_:
ld a,e
cp L
jr c,_ ;same deal as above, since max width and height are the same as in X
cp H
ret c
_: ;if we reach here, sanity has been checked. Sprite is displayable.
ld L,c ;sprite ID selector in L for now, since C is being overwritten
ld bc,$0800 ;B=loop counter, C=offset modifier
ld a,e ;getting Y position.
cp 57 ;checking to see if is on either right or left edge
jr c,DrawClippedSpriteSkipYClip
jp m,_ ;jumping if result is still negative (top side clipping)
;Bottom-side clipping
;57=7, 58=6 59=5 60=4 61=3 62=2 63=1. Just limit loop counter.
cpl ;-58 -59 -60 -61 -62 -63 -64
add a,65 ;7 6 5 4 3 2 1
ld b,a ;new loop counter established
jp DrawClippedSpriteSkipYClip
_: ;This is the top-side clipping routine
;IN:CNT:OFFSET -1:7:1 -2:6:2 -3:5:3 -4:4:4 -5:3:5 -6:2:6 -7:1:6 NDS:NDS:NDS
neg
ld c,a ;offset established. Already? Wow.
cpl ;-2:7 -3:6 -4:5 -5:4 -6:3 -7:2 -8:1
add a,9 ;Yes.
ld b,a ;Loop counter achieved.
ld e,0 ;set new Y position at 0 to allow proper clipping
DrawClippedSpriteSkipYClip:
push bc ;save loop counter, RCL by pop af. Use C offset soon.
ld a,d ;now, let's start processing these coordinates, shall we?
and %00000111
ld b,a ;save for getting mask address
add a,a
add a,a
add a,a ;x8 %00111000
add a,L ;merge to get correct sprite position
add a,c ;add sprite offset for Y clipping
ld c,a ;position saved
ld a,b
add a,8 ;getting spritemask position
ld h,$40
ld L,a
ld b,(hl) ;B=spritemask
;At this point, C=spritepostionLSB, B=mask DE=XY,
ld L,e ;y
ld h,0
add hl,hl
add hl,hl
add hl,hl
ld e,c ;sprite positioner. C is free'd up, using E as LSB to complete addr
ld a,b ;mask (left side AND)
cpl ;invert it
ld c,a ;mask (right side AND)
ld a,d ;check x position for clippage.
cp 57 ;Check to see if X needs to be clipped
jr c,DrawClippedSpriteSkipXClip
jp m,_ ;If still negative, then jump to left side clippage.
;Right side clipping.
ld a,7 ;manually set to right side of screen
ld b,0 ;manually clear right side mask to avoid showing sprite on left
jp DrawClippedSpriteManualSetX
_:
ld c,0 ;manually clearing left side mask to avoid showing sprite on right
dec hl
bit 7,h ;checking to see if HL went negative
jr z,DrawClippedSpriteManualSetHL
ld hl,$7FFF ;handling corner case where clipping at top-left edge
jp DrawClippedSpriteManualSetEdge ;It's a freaking literal corner!
DrawClippedSpriteSkipXClip:
rrca
rrca
rrca
and %00000111
DrawClippedSpriteManualSetX:
add a,L
ld L,a
DrawClippedSpriteManualSetHL:
set 7,h ;83cc
DrawClippedSpriteManualSetEdge:
ld d,$87 ;D=MSB of sprite position, E already is LSB. Address completed.
;So what we have so far is: C=LSMask, B=RSMask DE=sprAddr HL=scrnAddr
pop af ;A=loop counter we saved from so long ago.
ld (itemp2),sp
ld sp,7 ;SP = using in place of DE for advancing HL.
DrawClippedSpriteDrawLoop:
ex af,af'
ld a,(de) ;FINALLY. We are writing out that sprite in accordance to
and c ;all of our carefully laid out plans. Of course... plans
or (hl) ;tend to never survive initial contact. Oh, well. That's
ld (hl),a ;what the debugging phase is for.
inc hl
ld a,(de)
and b ;These comments have nothing to do with this particular stretch
or (hl) ;of code. Instead, I'm using this area to chronicle the debug
ld (hl),a ;phase. (1) Incomplete sanity check. Forgot to set HL fully.
add hl,sp ;(2) Bad inputs and script system incompatibility. (itemp1)
inc e ;needed to be preserved, and characte X,Y had subpixel data that
ex af,af' ;I needed to get rid of. (3) Reversed masks. (4) Top-left corner
dec a ;case addressed. Debugger used to find the problem. All fixed.
jr nz,DrawClippedSpriteDrawLoop
ld sp,(itemp2)
ret ;If your scale color is red, you're in good claws.
Sprite rendering is done during the stage script, purely for testing purposes. This is the code I've used (the whole thing, including the lines going across the screen; relevant code near bottom) :
Code: [Select]
StageScript:
.load(r1,-30) ;x
.load(r2,-30) ;y
.load(r3,30) ;loop range
.load(r4,1)
StgScptLoop:
.call(TestSpriteRoutine)
.load(r7,2)
.wait(r7)
.newposxy(r1,r2)
.call(CustomMakeBullet)
.cpl(r1)
.inc(r1)
.newposxy(r1,r2)
.call(CustomMakeBullet)
.setstats(r0,rs.debug1)
.cpl(r1)
.inc(r1)
.addrx(r1,r4)
.dec(r3)
.jumpnz(_)
.load(r3,30)
.cpl(r4)
.inc(r4)
_:
.jump(StgScptLoop)
CustomMakeBullet:
.runasm(_)
bit fbt1full,(iy+stateflags)
jr nz,SSSKIP
;Include and call ionRandom for varying bullet speeds when that test
;is about to be performed
ld c,%10000011
ld hl,freebullet1
ld e,(hl) ;1.READ
inc (hl) ;2.INC
ld d,$8C
ld a,(de)
jp p,$+7
set fbt1full,(iy+stateflags)
ld h,$88>>2
add a,a
ld L,a
add hl,hl
add hl,hl
xor a
ld (hl),CLSB(TestEBTShot)
inc L
ld (hl),CMSB(TestEBTShot)
inc L
ld (hl),c ;acc/TTL
inc L
ld (hl),b ;angle (allow to be uninitialized for now)
inc L
ld bc,(curpos) ;C=Y,B=X
ld (hl),a ;*.8 set to zero
inc L
ld (hl),b ;8.* X position
inc L
ld (hl),a ;*.8 also set to zero
inc L
ld (hl),c ;8.* Y position. Finished writing
SSSKIP:
ld a,(characterID)
ld (ix+0),a
jp r.runasm.ret
_:
.return
TestSpriteRoutine: ;so ruinous. Just total hell is about to be unleashed.
.runasm(_)
ld de,(chary)
srl e
srl e
srl d
srl d
ld a,-10 \ call TestSpriteRoutineAddToY ;up above
ld a,-10 \ call TestSpriteRoutineAddToX ;top-left
ld a,10 \ call TestSpriteRoutineAddToY ;left
ld a,10 \ call TestSpriteRoutineAddToY ;bottom-left
ld a,10 \ call TestSpriteRoutineAddToX ;bottom
ld a,10 \ call TestSpriteRoutineAddToX ;bottom-right
ld a,-10 \ call TestSpriteRoutineAddToY ;right
ld a,-10 \ call TestSpriteRoutineAddToY ;top-right
jp r.runasm.ret
TestSpriteRoutineAddToY:
add a,e
ld e,a
push de
xor a
call DrawClippedSprite
pop de
ret
TestSpriteRoutineAddToX:
add a,d
ld d,a
push de
xor a
call DrawClippedSprite
pop de
ret
_:
.return
Yes, I *am* aware that there's way more Z80 ASM code than there is CaDan script code. See, the stage script became mine and Geekboy's best friend. A great way to test out assembly code.And attached to the end of the post is a screenshot of the actual test.