/*************************************************************************** * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team * * schultz.ryan@gmail.com, http://rschultz.ath.cx/code.php * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA. * ***************************************************************************/ /* * Movie decoder. Based on the FPSE v0.08 Mdec decoder. */ #include "mdec.h" #define FIXED #define CONST_BITS 8 #define PASS1_BITS 2 #define FIX_1_082392200 (277) #define FIX_1_414213562 (362) #define FIX_1_847759065 (473) #define FIX_2_613125930 (669) #define MULTIPLY(var, const) (DESCALE((var) * (const), CONST_BITS)) #define DEQUANTIZE(coef, quantval) (coef) #define DESCALE(x, n) ((x) >> (n)) #define RANGE(n) (n) #define DCTSIZE 8 #define DCTSIZE2 64 static struct { u32 command; u32 status; u16 *rl; int rlsize; } mdec; static int iq_y[DCTSIZE2], iq_uv[DCTSIZE2]; static void idct1(int *block) { int i, val = RANGE(DESCALE(block[0], PASS1_BITS + 3)); for (i = 0; i < DCTSIZE2; i++) block[i] = val; } static void idct(int *block, int k) { int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; int z5, z10, z11, z12, z13; int *ptr; int i; if (!k) { idct1(block); return; } ptr = block; for (i = 0; i< DCTSIZE; i++, ptr++) { if ((ptr[DCTSIZE * 1] | ptr[DCTSIZE * 2] | ptr[DCTSIZE * 3] | ptr[DCTSIZE * 4] | ptr[DCTSIZE * 5] | ptr[DCTSIZE * 6] | ptr[DCTSIZE * 7]) == 0) { ptr[DCTSIZE * 0] = ptr[DCTSIZE * 1] = ptr[DCTSIZE * 2] = ptr[DCTSIZE * 3] = ptr[DCTSIZE * 4] = ptr[DCTSIZE * 5] = ptr[DCTSIZE * 6] = ptr[DCTSIZE * 7] = ptr[DCTSIZE * 0]; continue; } z10 = ptr[DCTSIZE * 0] + ptr[DCTSIZE * 4]; z11 = ptr[DCTSIZE * 0] - ptr[DCTSIZE * 4]; z13 = ptr[DCTSIZE * 2] + ptr[DCTSIZE * 6]; z12 = MULTIPLY(ptr[DCTSIZE * 2] - ptr[DCTSIZE * 6], FIX_1_414213562) - z13; tmp0 = z10 + z13; tmp3 = z10 - z13; tmp1 = z11 + z12; tmp2 = z11 - z12; z13 = ptr[DCTSIZE * 3] + ptr[DCTSIZE * 5]; z10 = ptr[DCTSIZE * 3] - ptr[DCTSIZE * 5]; z11 = ptr[DCTSIZE * 1] + ptr[DCTSIZE * 7]; z12 = ptr[DCTSIZE * 1] - ptr[DCTSIZE * 7]; z5 = MULTIPLY(z12 - z10, FIX_1_847759065); tmp7 = z11 + z13; tmp6 = MULTIPLY(z10, FIX_2_613125930) + z5 - tmp7; tmp5 = MULTIPLY(z11 - z13, FIX_1_414213562) - tmp6; tmp4 = MULTIPLY(z12, FIX_1_082392200) - z5 + tmp5; ptr[DCTSIZE * 0] = (tmp0 + tmp7); ptr[DCTSIZE * 7] = (tmp0 - tmp7); ptr[DCTSIZE * 1] = (tmp1 + tmp6); ptr[DCTSIZE * 6] = (tmp1 - tmp6); ptr[DCTSIZE * 2] = (tmp2 + tmp5); ptr[DCTSIZE * 5] = (tmp2 - tmp5); ptr[DCTSIZE * 4] = (tmp3 + tmp4); ptr[DCTSIZE * 3] = (tmp3 - tmp4); } ptr = block; for (i = 0; i < DCTSIZE; i++, ptr += DCTSIZE) { if ((ptr[1] | ptr[2] | ptr[3] | ptr[4] | ptr[5] | ptr[6] | ptr[7]) == 0) { ptr[0] = ptr[1] = ptr[2] = ptr[3] = ptr[4] = ptr[5] = ptr[6] = ptr[7] = RANGE(DESCALE(ptr[0], PASS1_BITS + 3)); continue; } z10 = ptr[0] + ptr[4]; z11 = ptr[0] - ptr[4]; z13 = ptr[2] + ptr[6]; z12 = MULTIPLY(ptr[2] - ptr[6], FIX_1_414213562) - z13; tmp0 = z10 + z13; tmp3 = z10 - z13; tmp1 = z11 + z12; tmp2 = z11 - z12; z13 = ptr[3] + ptr[5]; z10 = ptr[3] - ptr[5]; z11 = ptr[1] + ptr[7]; z12 = ptr[1] - ptr[7]; z5 = MULTIPLY(z12 - z10, FIX_1_847759065); tmp7 = z11 + z13; tmp6 = MULTIPLY(z10, FIX_2_613125930) + z5 - tmp7; tmp5 = MULTIPLY(z11 - z13, FIX_1_414213562) - tmp6; tmp4 = MULTIPLY(z12, FIX_1_082392200) - z5 + tmp5; ptr[0] = RANGE(DESCALE(tmp0 + tmp7, PASS1_BITS+3));; ptr[7] = RANGE(DESCALE(tmp0 - tmp7, PASS1_BITS+3));; ptr[1] = RANGE(DESCALE(tmp1 + tmp6, PASS1_BITS+3));; ptr[6] = RANGE(DESCALE(tmp1 - tmp6, PASS1_BITS+3));; ptr[2] = RANGE(DESCALE(tmp2 + tmp5, PASS1_BITS+3));; ptr[5] = RANGE(DESCALE(tmp2 - tmp5, PASS1_BITS+3));; ptr[4] = RANGE(DESCALE(tmp3 + tmp4, PASS1_BITS+3));; ptr[3] = RANGE(DESCALE(tmp3 - tmp4, PASS1_BITS+3));; } } #define RUNOF(a) ((a) >> 10) #define VALOF(a) (((int)(a) << (32 - 10)) >> (32 - 10)) static int zscan[DCTSIZE2] = { 0 , 1 , 8 , 16, 9 , 2 , 3 , 10, 17, 24, 32, 25, 18, 11, 4 , 5 , 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6 , 7 , 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; static int aanscales[DCTSIZE2] = { 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 }; static void iqtab_init(int *iqtab, unsigned char *iq_y) { #define CONST_BITS14 14 #define IFAST_SCALE_BITS 2 int i; for(i = 0; i < DCTSIZE2; i++) { iqtab[i] = iq_y[i] * aanscales[zscan[i]] >> (CONST_BITS14 - IFAST_SCALE_BITS); } } #define NOP 0xfe00 static unsigned short *rl2blk(int *blk, unsigned short *mdec_rl) { int i, k, q_scale, rl; int *iqtab; memset(blk, 0, 6 * DCTSIZE2 * 4); iqtab = iq_uv; for (i = 0; i < 6; i++) { // decode blocks (Cr,Cb,Y1,Y2,Y3,Y4) if (i > 1) iqtab = iq_y; // zigzag transformation rl = SWAP16(*mdec_rl); mdec_rl++; q_scale = RUNOF(rl); blk[0] = iqtab[0] * VALOF(rl); for (k = 0;;) { rl = SWAP16(*mdec_rl); mdec_rl++; if (rl == NOP) break; k += RUNOF(rl) + 1; // skip level zero-coefficients if (k > 63) break; blk[zscan[k]] = (VALOF(rl) * iqtab[k] * q_scale) / 8; // / 16; } // blk[0] = (blk[0] * iq_t[0] * 8) / 16; // for(int j = 1; j<64; j++) // blk[j] = blk[j] * iq_t[j] * q_scale; idct(blk, k + 1); blk += DCTSIZE2; } return mdec_rl; } #ifdef FIXED #define MULR(a) ((((int)0x0000059B) * (a)) >> 10) #define MULG(a) ((((int)0xFFFFFEA1) * (a)) >> 10) #define MULG2(a) ((((int)0xFFFFFD25) * (a)) >> 10) #define MULB(a) ((((int)0x00000716) * (a)) >> 10) #else #define MULR(a) ((int)((float)1.40200 * (a))) #define MULG(a) ((int)((float)-0.3437 * (a))) #define MULG2(a) ((int)((float)-0.7143 * (a))) #define MULB(a) ((int)((float)1.77200 * (a))) #endif #define MAKERGB15(r, g, b) ( SWAP16((((r) >> 3) << 10)|(((g) >> 3) << 5)|((b) >> 3)) ) #define ROUND(c) ( ((c) < -128) ? 0 : (((c) > (255 - 128)) ? 255 : ((c) + 128)) ) #define RGB15(n, Y) \ image[n] = MAKERGB15(ROUND(Y + R), ROUND(Y + G), ROUND(Y + B)); #define RGB15BW(n, Y) \ image[n] = MAKERGB15(ROUND(Y), ROUND(Y), ROUND(Y)); #define RGB24(n, Y) \ image[n + 2] = ROUND(Y + R); \ image[n + 1] = ROUND(Y + G); \ image[n + 0] = ROUND(Y + B); #define RGB24BW(n, Y) \ image[n + 2] = ROUND(Y); \ image[n + 1] = ROUND(Y); \ image[n + 0] = ROUND(Y); static void yuv2rgb15(int *blk, unsigned short *image) { int x, y; int *Yblk = blk + DCTSIZE2 * 2; int Cb, Cr, R, G, B; int *Cbblk = blk; int *Crblk = blk + DCTSIZE2; if (!Config.Mdec) { for (y = 0; y < 16; y += 2, Crblk += 4, Cbblk += 4, Yblk += 8, image += 24) { if (y == 8) Yblk += DCTSIZE2; for (x = 0; x < 4; x++, image += 2, Crblk++, Cbblk++, Yblk += 2) { Cr = *Crblk; Cb = *Cbblk; R = MULR(Cr); G = MULG(Cb) + MULG2(Cr); B = MULB(Cb); RGB15(0, Yblk[0]); RGB15(1, Yblk[1]); RGB15(16, Yblk[8]); RGB15(17, Yblk[9]); Cr = *(Crblk + 4); Cb = *(Cbblk + 4); R = MULR(Cr); G = MULG(Cb) + MULG2(Cr); B = MULB(Cb); RGB15(8, Yblk[DCTSIZE2 + 0]); RGB15(9, Yblk[DCTSIZE2 + 1]); RGB15(24, Yblk[DCTSIZE2 + 8]); RGB15(25, Yblk[DCTSIZE2 + 9]); } } } else { for (y = 0; y < 16; y += 2, Yblk += 8, image += 24) { if (y == 8) Yblk += DCTSIZE2; for (x = 0; x < 4; x++, image += 2, Yblk += 2) { RGB15BW(0, Yblk[0]); RGB15BW(1, Yblk[1]); RGB15BW(16, Yblk[8]); RGB15BW(17, Yblk[9]); RGB15BW(8, Yblk[DCTSIZE2 + 0]); RGB15BW(9, Yblk[DCTSIZE2 + 1]); RGB15BW(24, Yblk[DCTSIZE2 + 8]); RGB15BW(25, Yblk[DCTSIZE2 + 9]); } } } } static void yuv2rgb24(int *blk, unsigned char *image) { int x,y; int *Yblk = blk + DCTSIZE2 * 2; int Cb, Cr, R, G, B; int *Cbblk = blk; int *Crblk = blk + DCTSIZE2; if (!Config.Mdec) { for (y = 0; y < 16; y += 2, Crblk += 4, Cbblk += 4, Yblk += 8, image += 24 * 3) { if (y == 8) Yblk += DCTSIZE2; for (x = 0; x < 4; x++, image += 6, Crblk++, Cbblk++, Yblk += 2) { Cr = *Crblk; Cb = *Cbblk; R = MULR(Cr); G = MULG(Cb) + MULG2(Cr); B = MULB(Cb); RGB24(0, Yblk[0]); RGB24(1 * 3, Yblk[1]); RGB24(16 * 3, Yblk[8]); RGB24(17 * 3, Yblk[9]); Cr = *(Crblk + 4); Cb = *(Cbblk + 4); R = MULR(Cr); G = MULG(Cb) + MULG2(Cr); B = MULB(Cb); RGB24(8 * 3, Yblk[DCTSIZE2 + 0]); RGB24(9 * 3, Yblk[DCTSIZE2 + 1]); RGB24(24 * 3, Yblk[DCTSIZE2 + 8]); RGB24(25 * 3, Yblk[DCTSIZE2 + 9]); } } } else { for (y = 0; y < 16; y += 2, Yblk += 8, image += 24 * 3) { if (y == 8) Yblk += DCTSIZE2; for (x = 0; x < 4; x++, image += 6, Yblk += 2) { RGB24BW(0, Yblk[0]); RGB24BW(1 * 3, Yblk[1]); RGB24BW(16 * 3, Yblk[8]); RGB24BW(17 * 3, Yblk[9]); RGB24BW(8 * 3, Yblk[DCTSIZE2 + 0]); RGB24BW(9 * 3, Yblk[DCTSIZE2 + 1]); RGB24BW(24 * 3, Yblk[DCTSIZE2 + 8]); RGB24BW(25 * 3, Yblk[DCTSIZE2 + 9]); } } } } void mdecInit(void) { mdec.rl = (u16 *)&psxM[0x100000]; mdec.command = 0; mdec.status = 0; } void mdecWrite0(u32 data) { #ifdef CDR_LOG CDR_LOG("mdec0 write %lx\n", data); #endif mdec.command = data; if ((data & 0xf5ff0000) == 0x30000000) { mdec.rlsize = data & 0xffff; } } void mdecWrite1(u32 data) { #ifdef CDR_LOG CDR_LOG("mdec1 write %lx\n", data); #endif if (data & 0x80000000) { // mdec reset mdec.command = 0; mdec.status = 0; } } u32 mdecRead0(void) { #ifdef CDR_LOG CDR_LOG("mdec0 read %lx\n", mdec.command); #endif return mdec.command; } // mdec status: #define MDEC_BUSY 0x20000000 #define MDEC_DREQ 0x18000000 #define MDEC_FIFO 0xc0000000 #define MDEC_RGB24 0x02000000 #define MDEC_STP 0x00800000 u32 mdecRead1(void) { #ifdef CDR_LOG CDR_LOG("mdec1 read %lx\n", mdec.status); #endif return mdec.status; } void psxDma0(u32 adr, u32 bcr, u32 chcr) { int cmd = mdec.command; int size; #ifdef CDR_LOG CDR_LOG("DMA0 %lx %lx %lx\n", adr, bcr, chcr); #endif if (chcr != 0x01000201) return; size = (bcr >> 16) * (bcr & 0xffff); if (cmd == 0x60000000) { } else if (cmd == 0x40000001) { u8 *p = (u8 *)PSXM(adr); iqtab_init(iq_y, p); iqtab_init(iq_uv, p + 64); } else if ((cmd & 0xf5ff0000) == 0x30000000) { mdec.rl = (u16 *)PSXM(adr); } else { } HW_DMA0_CHCR &= SWAP32(~0x01000000); DMA_INTERRUPT(0); } void psxDma1(u32 adr, u32 bcr, u32 chcr) { int blk[DCTSIZE2 * 6]; unsigned short *image; int size; #ifdef CDR_LOG CDR_LOG("DMA1 %lx %lx %lx (cmd = %lx)\n", adr, bcr, chcr, mdec.command); #endif if (chcr != 0x01000200) return; size = (bcr >> 16) * (bcr & 0xffff); image = (u16 *)PSXM(adr); if (mdec.command & 0x08000000) { // MDECOUTDMA_INT(((size * (1000000 / 9000)) / 4) /** 4*/ / BIAS); MDECOUTDMA_INT((size / 4) / BIAS); size = size / ((16 * 16)/2); for (; size > 0; size--, image += (16 * 16)) { mdec.rl = rl2blk(blk, mdec.rl); yuv2rgb15(blk, image); } } else { // MDECOUTDMA_INT(((size * (1000000 / 9000)) / 4) /** 4*/ / BIAS); MDECOUTDMA_INT((size / 4) / BIAS); size = size / ((24 * 16) / 2); for (; size > 0; size--, image += (24 * 16)) { mdec.rl = rl2blk(blk, mdec.rl); yuv2rgb24(blk, (u8 *)image); } } mdec.status |= MDEC_BUSY; } void mdec1Interrupt() { #ifdef CDR_LOG CDR_LOG("mdec1Interrupt\n"); #endif if (HW_DMA1_CHCR & SWAP32(0x01000000)) { // Set a fixed value totaly arbitrarie another sound value is PSXCLK / 60 // or PSXCLK / 50 since the bug happend at end of frame. PSXCLK / 1000 seems // good for FF9. (for FF9 need < ~28000) // CAUTION: commented interrupt-handling may lead to problems, keep an eye ;) MDECOUTDMA_INT(PSXCLK / 1000); //psxRegs.interrupt |= 0x02000000; //psxRegs.intCycle[5 + 24 + 1] *= 8; //psxRegs.intCycle[5 + 24] = psxRegs.cycle; HW_DMA1_CHCR &= SWAP32(~0x01000000); DMA_INTERRUPT(1); } else { mdec.status &= ~MDEC_BUSY; } } int mdecFreeze(gzFile f, int Mode) { char Unused[4096]; gzfreeze(&mdec, sizeof(mdec)); gzfreezel(iq_y); gzfreezel(iq_uv); gzfreezel(Unused); return 0; }