/*---------------------------------------------------------------------* Copyright (C) 1998, Nintendo. File us2dex_emu.c Coded by Yoshitaka Yasumoto. Apr 9, 1998. $Id: us2dex_emu.c,v 1.9 1999/07/08 05:02:04 yurugi Exp $ *---------------------------------------------------------------------*/ #define F3DEX_GBI #include #include #define RSP_DEBUG static u16 scissorX0 = 0; /* (s13.2) */ static u16 scissorY0 = 0; /* (s13.2) */ static u16 scissorX1 = 320<<2; /* (s13.2) */ static u16 scissorY1 = 240<<2; /* (s13.2) */ static u8 flagBilerp = 0; static u32 rdpSetTimg_w0, rdpSetTile_w0; static u16 tmemSliceWmax; static u16 imageSrcWsize; static s16 flagSplit; static u16 imagePtrX0; static u32 imageTop; static s16 tmemSrcLines; /* Addition 99/05/31(Y) */ #if BUILD_VERSION >= VERSION_K static s8 bgflg; #endif /*----------------------------------------------------------------------------* * Set scissoring parameters * *---------------------------------------------------------------------------*/ void guS2DEmuSetScissor(u32 ulx, u32 uly, u32 lrx, u32 lry, u8 flag) { scissorX0 = (u16)ulx << 2; scissorY0 = (u16)uly << 2; scissorX1 = (u16)lrx << 2; scissorY1 = (u16)lry << 2; flagBilerp = (flag) ? 1 : 0; } /*---------------------------------------------------------------------------* * Create texture load RDP commands *---------------------------------------------------------------------------*/ static void tmemLoad_B(Gfx **pkt, u32 imagePtr, s16 loadLines, s16 tmemSH) { /* * Load 16-bit texture of tmemSH word width starting from imagePtr * into the loadLines amount of lines of tmem. */ /* [SetTImg] CMD=0x3d FMT=RGBA(0) SIZ=16b(2) */ (*pkt)->words.w0 = rdpSetTimg_w0; (*pkt)->words.w1 = imagePtr; (*pkt) ++; /* [LoadSync] Wait for completion of preceding primitive draw */ (*pkt)->words.w0 = (G_RDPLOADSYNC<<24); (*pkt) ++; /* [LoadTile] CMD=0x34 TILE=7 SH=TMEMW*16-1 TMEMH*4-1 */ (*pkt)->words.w0 = (G_LOADTILE<<24)|0x000000; /* addition 99/5/31(Y) */ #if BUILD_VERSION >= VERSION_K if(bgflg==3) (*pkt)->words.w1 = 0x07000000 | (tmemSH-1)<<16; else (*pkt)->words.w1 = 0x07000000 | (tmemSH-1)<<16 | (loadLines<<2)-1; #else (*pkt)->words.w1 = 0x07000000 | (tmemSH-1)<<16 | (loadLines<<2)-1; #endif (*pkt) ++; } static void tmemLoad_A(Gfx **pkt, u32 imagePtr, s16 loadLines, s16 tmemAdrs, s16 tmemSH) { /* * Load 16-bit texture of tmemSH word width starting from imagePtr into * the loadLines amount of lines of the tmemAdrs of tmem. */ /* [TileSync] Wait for completion of Tile access of preceding command */ (*pkt)->words.w0 = 0xe8000000; (*pkt) ++; /* [SetTile] */ (*pkt)->words.w0 = rdpSetTile_w0|tmemAdrs; (*pkt)->words.w1 = 0x07000000; (*pkt) ++; tmemLoad_B(pkt, imagePtr, loadLines, tmemSH); } static void tmemLoad(Gfx **pkt, u32 *imagePtr, s16 *imageRemain, s16 drawLines, s16 flagBilerp) { s16 loadLines = drawLines + flagBilerp; s16 iLoadable = (*imageRemain) - flagSplit; if (iLoadable >= loadLines){ /* If load can be done all at once */ tmemLoad_B(pkt, *imagePtr, loadLines, tmemSliceWmax); (*imagePtr) += imageSrcWsize * drawLines; (*imageRemain) -= drawLines; } else { /* If load is to be partitioned */ s16 SubSliceL2, SubSliceD2, SubSliceY2; u32 imageTopSeg = imageTop & 0xff000000; SubSliceY2 = *imageRemain; SubSliceL2 = loadLines - SubSliceY2; SubSliceD2 = drawLines - SubSliceY2; if (SubSliceL2 > 0){ u32 imagePtr2; imagePtr2 = imageTop + imagePtrX0; if (SubSliceY2 & 1){ imagePtr2 -= imageSrcWsize; imagePtr2 = imageTopSeg | (imagePtr2 & 0x00ffffff); /*Segment counterm */ SubSliceY2 --; SubSliceL2 ++; } tmemLoad_A(pkt, imagePtr2, SubSliceL2, SubSliceY2 * tmemSliceWmax, tmemSliceWmax); } if (flagSplit){ u32 imagePtr1A, imagePtr1B; s16 SubSliceY1, SubSliceL1; s16 tmemSH_A, tmemSH_B; imagePtr1A = (*imagePtr) + iLoadable * imageSrcWsize; imagePtr1B = imageTop; SubSliceY1 = iLoadable; if (SubSliceL1 = iLoadable & 1){ imagePtr1A -= imageSrcWsize; imagePtr1B -= imageSrcWsize; imagePtr1B = imageTopSeg | (imagePtr1B & 0x00ffffff); /*Segment countermeasure */ SubSliceY1 --; } SubSliceL1 ++; tmemSH_A = (imageSrcWsize - imagePtrX0) >> 3; tmemSH_B = tmemSliceWmax - tmemSH_A; tmemLoad_A(pkt, imagePtr1B, SubSliceL1, SubSliceY1 * tmemSliceWmax + tmemSH_A, tmemSH_B); tmemLoad_A(pkt, imagePtr1A, SubSliceL1, SubSliceY1 * tmemSliceWmax, tmemSH_A); } if (iLoadable > 0){ tmemLoad_A(pkt, *imagePtr, iLoadable, 0, tmemSliceWmax); } else { /* [SetTile] */ (*pkt)->words.w0 = rdpSetTile_w0; (*pkt)->words.w1 = 0x07000000; (*pkt) ++; } (*imageRemain) -= drawLines; if ((*imageRemain) > 0){ (*imagePtr) += imageSrcWsize * drawLines; } else { (*imageRemain) = tmemSrcLines - SubSliceD2; (*imagePtr) = imageTop + SubSliceD2 * imageSrcWsize + imagePtrX0; } } } /*---------------------------------------------------------------------------* * Scalable BG serface draw process *---------------------------------------------------------------------------*/ void guS2DEmuBgRect1Cyc(Gfx **pkt, uObjBg *bg) { s16 frameX0, frameX1, framePtrY0, frameRemain; s16 imageX0, imageY0, imageSliceW, imageW; s32 imageYorig; s16 scaleW, scaleH; s16 imageSrcW, imageSrcH; s16 tmemSliceLines, imageSliceLines; s32 frameSliceLines, frameSliceCount; u16 imageS, imageT; u32 imagePtr; s16 imageISliceL0, imageIY0; s32 frameLSliceL0; scaleW = bg->s.scaleW; scaleH = bg->s.scaleH; /* addition 99/05/31(Y) */ #if BUILD_VERSION >= VERSION_K bgflg=bg->s.imageSiz; if(scaleW==0) scaleW=1; if(scaleH==0) scaleH=1; #endif { /*-------------------------------------------------* Scissoring process *-------------------------------------------------*/ /* / frameX0, frameX1, framePtrY0, frameRemain / imageX0, imageY0, tmemSliceW / imageSrcW, imageSrcH */ s16 pixX0, pixY0, pixX1, pixY1; /* (s13.2) */ s16 frameY0, frameW, frameH; s32 frameWmax, frameHmax; /* Determine maximum frame size from image and enlargement ratio */ frameWmax = ((((s32)bg->s.imageW << 10) / scaleW)-1) & ~3; frameHmax = ((((s32)bg->s.imageH << 10) / scaleH)-1) & ~3; /* Clamp the frame size */ frameW = bg->s.frameW; frameH = bg->s.frameH; frameX0 = bg->s.frameX; frameY0 = bg->s.frameY; if ((frameWmax = bg->s.frameW - frameWmax) < 0) frameWmax = 0; if ((frameHmax = bg->s.frameH - frameHmax) < 0) frameHmax = 0; frameW -= frameWmax; frameH -= frameHmax; if (bg->s.imageFlip & G_BG_FLAG_FLIPS){ frameX0 += frameWmax; } /* Caluculate amount of projection from screen */ pixX0 = scissorX0 - frameX0; /* (s13.2) */ pixY0 = scissorY0 - frameY0; /* (s13.2) */ pixX1 = frameW - scissorX1 + frameX0; /* (s13.2) */ pixY1 = frameH - scissorY1 + frameY0; /* (s13.2) */ /* Clamp if no projection */ if (pixX0 < 0) pixX0 = 0; if (pixY0 < 0) pixY0 = 0; if (pixX1 < 0) pixX1 = 0; if (pixY1 < 0) pixY1 = 0; /* Cut the part that is projecting */ frameW = frameW - (pixX0 + pixX1); /* (s13.2) */ frameH = frameH - (pixY0 + pixY1); /* (s13.2) */ frameX0 = frameX0 + pixX0; /* (s13.2) */ frameY0 = frameY0 + pixY0; /* (s13.2) */ /* Terminate if no draw range remains */ if (frameW <= 0 || frameH <= 0) return; /* Caluculate frame */ frameX1 = frameX0 + frameW; /* (s13.2) */ framePtrY0 = frameY0 >> 2; /* (s15.0) */ frameRemain = frameH >> 2; /* (s15.0) */ /* Join image columns */ imageSrcW = bg->s.imageW << 3; /* (u11.5) */ imageSrcH = bg->s.imageH << 3; /* (u11.5) */ /* Get image range (u14.2)*(u6.10)=(u20.12)->(u11.5) */ imageSliceW = (imageW = frameW * scaleW >> 7) + flagBilerp * 32; if (bg->s.imageFlip & G_BG_FLAG_FLIPS){ imageX0 = bg->s.imageX + (pixX1 * scaleW >> 7); /* (s10.5) */ } else { imageX0 = bg->s.imageX + (pixX0 * scaleW >> 7); /* (s10.5) */ } imageY0 = bg->s.imageY + (pixY0 * scaleH >> 7); /* (s10.5) */ imageYorig = bg->s.imageYorig; /* Loop one step down when left end of image area is greater than right end of image source */ while (imageX0 >= imageSrcW){ imageX0 -= imageSrcW; imageY0 += 32; imageYorig += 32; /* Add 1 to carrier */ } /* Loop when top end of image area is greater than bottom end of image source */ while (imageY0 >= imageSrcH){ imageY0 -= imageSrcH; imageYorig -= imageSrcH; } } { /*-------------------------------------------------* Check process connecting top and bottom of image *-------------------------------------------------*/ /* // flagSplit // tmemSrcLines // imageSrcLines */ /* Top/bottom connection process necessary when image range straddles right end */ flagSplit = (imageX0 + imageSliceW >= imageSrcW); /* Number of image lines that can be loaded at once */ tmemSrcLines = imageSrcH >> 5; } { /*-------------------------------------------------* Get load data for TMEM *-------------------------------------------------*/ /* //Calculate number of lines that can be loaded into TMEM. // If the slice width changes due to scissoring and the linked // load line number is changed, then the frame division line will // change and the result will be the generation of unnatural wrinkles. // To prevent this, the division region is set such that scissoring // does not occur. // */ s16 tmemSize, tmemMask, tmemShift; s32 imageNumSlice; s32 imageSliceWmax; s32 imageLYoffset, frameLYoffset; s32 imageLHidden, frameLHidden; s32 frameLYslice; static s16 TMEMSIZE[] = { 512, 512, 256, 512, 512 }; static s16 TMEMMASK[] = { 0x1ff, 0xff, 0x7f, 0x3f }; static s16 TMEMSHIFT[] = { 0x200, 0x100, 0x80, 0x40 }; tmemSize = TMEMSIZE[bg->s.imageFmt]; /* (s15.0) */ tmemMask = TMEMMASK[bg->s.imageSiz]; /* (s10.5) */ tmemShift = TMEMSHIFT[bg->s.imageSiz]; /* (s10.5)->(s15.0) */ /* Calculate tmem width to accommodate the slice image width */ /* o Extra is needed at Bilerp time. // o Clamp slice image width based on image source width. // o Cut image width end number at TMEM Word boundary. // o tmem width + 1 when start position not in agreement with Word boundary */ /* addition 99/05/31(Y) */ #if BUILD_VERSION >= VERSION_K if(bgflg==3){ tmemSize=480; imageSliceWmax = 0x2800; } else{ imageSliceWmax = (((s32)bg->s.frameW * (s32)scaleW)>>7) + (flagBilerp<<5); if (imageSliceWmax > imageSrcW) imageSliceWmax = imageSrcW; } #else imageSliceWmax = (((s32)bg->s.frameW * (s32)scaleW)>>7) + (flagBilerp<<5); if (imageSliceWmax > imageSrcW) imageSliceWmax = imageSrcW; #endif tmemSliceWmax = (imageSliceWmax + tmemMask) / tmemShift + 1; /* Get TMEM/image/frame line number that can be loaded at once */ tmemSliceLines = tmemSize / tmemSliceWmax; /* (s15.0) */ imageSliceLines = tmemSliceLines - flagBilerp; /* (s15.0) */ frameSliceLines = (imageSliceLines << 20) / scaleH; /* (s21.10) */ /* Line up image Y coordinate offset value with frame image */ imageLYoffset = ((s32)imageY0 - imageYorig) << 5; if (imageLYoffset < 0) imageLYoffset -= (scaleH - 1); frameLYoffset = imageLYoffset / scaleH; frameLYoffset <<= 10; /* Get slice number corresponding to image Y */ if (frameLYoffset >= 0){ imageNumSlice = frameLYoffset / frameSliceLines; } else { imageNumSlice = (frameLYoffset - frameSliceLines + 1) / frameSliceLines; } /* Calculate extent to which first draw slice is hidden at top of frame */ frameLYslice = (frameLSliceL0 = frameSliceLines * imageNumSlice) & ~1023; frameLHidden = frameLYoffset - frameLYslice; imageLHidden = (frameLHidden >> 10) * scaleH; /* Calculate fram size of first draw slice */ frameLSliceL0 = (frameLSliceL0 & 1023) + frameSliceLines - frameLHidden; /* Calculate image parameters for draw in midst of slice */ imageT = (imageLHidden >> 5) & 31; imageLHidden >>= 10; imageISliceL0 = imageSliceLines - imageLHidden; imageIY0 = imageSliceLines * imageNumSlice + (imageYorig & ~31) / 32 + imageLHidden; if (imageIY0 < 0) imageIY0 += (bg->s.imageH >> 2); if (imageIY0 >= (bg->s.imageH >> 2)) imageIY0 -= (bg->s.imageH >> 2); imageTop = (u32)bg->s.imagePtr; /* (u32.0) */ imageSrcWsize = (imageSrcW / tmemShift) << 3; /* (u16.0) */ imagePtrX0 = (imageX0 / tmemShift) << 3; /* (u16.0) */ imagePtr = imageTop + imageSrcWsize * imageIY0 + imagePtrX0; /* S invert process */ imageS = imageX0 & tmemMask; /* (u10.5) */ if (bg->s.imageFlip & G_BG_FLAG_FLIPS){ imageS = - (imageS + imageW); } } { /*-------------------------------------------------* Creating RDP command constant values *-------------------------------------------------*/ /* u32 rdpSetTimg_w0; u32 rdpSetTile_w0; */ /* Addition 99/05/31(Y) */ #if BUILD_VERSION >= VERSION_K if(bgflg==3){ rdpSetTimg_w0 = (G_SETTIMG<<24)+0x180000+(imageSrcWsize>>1)-1; rdpSetTile_w0 = (G_SETTILE<<24)+0x180000+(tmemSliceWmax<<9); } else{ rdpSetTimg_w0 = (G_SETTIMG<<24)+0x100000+(imageSrcWsize>>1)-1; rdpSetTile_w0 = (G_SETTILE<<24)+0x100000+(tmemSliceWmax<<9); } #else rdpSetTimg_w0 = (G_SETTIMG<<24)+0x100000+(imageSrcWsize>>1)-1; rdpSetTile_w0 = (G_SETTILE<<24)+0x100000+(tmemSliceWmax<<9); #endif /* [SetTile:7] */ (*pkt)->words.w0 = rdpSetTile_w0; (*pkt)->words.w1 = 0x07000000; (*pkt)++; /* [SetTile:0] */ (*pkt)->words.w0 = rdpSetTile_w0; ((u8 *)&((*pkt)->words.w0))[1] = (bg->s.imageFmt<<5)|(bg->s.imageSiz<<3); (*pkt)->words.w1 = 0x0007c1f0 | (bg->s.imagePal<<20); (*pkt)++; /* [SetTileSize:7] */ (*pkt)->words.w0 = (G_SETTILESIZE<<24)|0x000000; (*pkt)->words.w1 = 0x00000000; (*pkt)++; } { s16 imageRemain; s16 imageSliceH, frameSliceH; imageRemain = tmemSrcLines - imageIY0; imageSliceH = imageISliceL0; frameSliceCount = frameLSliceL0; while(1){ /* Calculate draw line number and determine whether to perform a draw */ frameSliceH = frameSliceCount >> 10; if (frameSliceH <= 0){ /* If no draw to be performed */ imageRemain -= imageSliceH; if (imageRemain > 0){ imagePtr += imageSrcWsize * imageSliceH; } else { imagePtr = imageTop - (imageRemain * imageSrcWsize) + imagePtrX0; imageRemain += tmemSrcLines; } } else { /* If draw to be performed */ s16 framePtrY1; frameSliceCount &= 1023; if ((frameRemain -= frameSliceH) < 0){ /* Determine final slice */ frameSliceH += frameRemain; imageSliceH += ((frameRemain * scaleH) >> 10) + 1; if (imageSliceH > imageSliceLines) imageSliceH = imageSliceLines; } tmemLoad(pkt, &imagePtr, &imageRemain, imageSliceH, flagBilerp); /* Get draw frame range */ framePtrY1 = framePtrY0 + frameSliceH; /* (s15.0) */ /* [PipeSync] Wait for end of preceding LOADTILE command */ (*pkt)->words.w0 = (G_RDPPIPESYNC<<24); (*pkt) ++; /* Rectangle draw */ /* Here an RSP command is created. With ucode an RDP command is created */ #if 1 { /* [TextureRectangle] */ (*pkt)->words.w0 = (G_TEXRECT<<24)|(frameX1<<12)|(framePtrY1<<2); (*pkt)->words.w1 = (frameX0<<12)|(framePtrY0<<2); (*pkt) ++; #else /* Code for checking slice division line */ if (frameSliceH > 1){ (*pkt)->words.w0 = (G_TEXRECT<<24)|(frameX1<<12)|(framePtrY1<<2)-4; (*pkt)->words.w1 = (frameX0<<12)|(framePtrY0<<2); (*pkt) ++; #endif #if 0 /* At RDP command creation time */ (*pkt)->words.w0 = (imageS<<16) | imageT; (*pkt)->words.w1 = (scaleW<<16) | scaleH; #else /* At RSP command creation time */ (*pkt)->words.w0 = (G_RDPHALF_1<<24); (*pkt)->words.w1 = (imageS<<16) | imageT; (*pkt) ++; (*pkt)->words.w0 = (G_RDPHALF_2<<24); (*pkt)->words.w1 = (scaleW<<16) | scaleH; #endif (*pkt) ++; } /* Update region */ framePtrY0 = framePtrY1; /* End if no more undrawn line numbers for frame */ if (frameRemain <= 0){ return; } } frameSliceCount += frameSliceLines; imageSliceH = imageSliceLines; imageT = 0; } } } /*======== End of us2dex_emu.c ========*/