/* Pcsx - Pc Psx Emulator * Copyright (C) 1999-2002 Pcsx Team * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This code was based on the FPSE v0.08 Mdec decoder*/ #include #include #include "PsxCommon.h" #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 void idct1(int *block) { int val = RANGE(DESCALE(block[0], PASS1_BITS+3)); int i; for(i=0;i>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 { } } 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) { for (;size>0;size-=(16*16)/2,image+=(16*16)) { mdec.rl = rl2blk(blk,mdec.rl); yuv2rgb15(blk,image); } } else { for (;size>0;size-=(24*16)/2,image+=(24*16)) { mdec.rl = rl2blk(blk,mdec.rl); yuv2rgb24(blk,(u8 *)image); } } } #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 }; void iqtab_init(int *iqtab,unsigned char *iq_y) { #define CONST_BITS14 14 #define IFAST_SCALE_BITS 2 int i; for(i=0;i>(CONST_BITS14-IFAST_SCALE_BITS); } } /* // From Filefrmt.pdf RL data syntax: header macroblock ... macroblock footer header: 16bit: magic (0x3800) 16bit: size macroblock: Cb block Cr block Y1 block Y2 block Y3 block Y4 block block: 5bit: quant, 10bit: dc 5bit: run, 10bit: level ... 5bit: run, 10bit: level nop (0xfe00) footer: nop block conversion: zigzag -> dequantize -> idct -> yuv2rgb zigzag transformation: the blk_zig value is the level value of the block blk_zig[0] = blk_dct[0]/iq_tab[0]; for (i = 1; i < 64; i++) { j = zscan[i]; blk_zig[i] = blk_dct[j]*16/(iqtab[j]*q_scale); } reverse it: blk_dct[0] = blk_zig[0]*iq_tab[0]; for (i = 1; i < 64; i++) { j = zscan[i]; blk_dct[j] = blk_zig[i]/(16/(iqtab[j]*q_scale)); } (run, level) (the number of zeros preceding level, value of the element) run-level example: -229 -19 0 -2 0 0 1 0 (0,-229) (0,-19) (1,-2) (2,1) nop dequantization: quant = q_scale y[0] = x[0] * 16 / (iqtab[0] * 8); for (i = 1; i < 64; i++) y[i] = x[i] / (quant * Qtable[i]); reverse it: x[0] = y[0] / (16 / (iqtab[0] * 8)); for (i = 1; i < 64; i++) x[i] = y[i] * (quant * Qtable[i]); idct: yuv2rgb: R = Y * 1.0 + Cb * 0 + Cr * 1.402 G = Y * 1.0 + Cb * -0.3437 + Cr * -0.7143 B = Y * 1.0 + Cb * 1.772 + Cr * 0 */ #define NOP 0xfe00 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 = *mdec_rl++; q_scale = RUNOF(rl); blk[0] = iqtab[0]*VALOF(rl); for(k = 0;;) { 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 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) ( (((r)>>3)<<10)|(((g)>>3)<<5)|((b)>>3) ) #define ROUND(c) roundtbl[((c)+128+256)]//&0x3ff] /*#define ROUND(c) round(c+128) int round(int r) { if (r<0) return 0; if (r>255) return 255; return r; }*/ #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); unsigned char roundtbl[256*3]; void round_init(void) { int i; for(i=0;i<256;i++) { roundtbl[i]=0; roundtbl[i+256]=i; roundtbl[i+512]=255; } } 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&0x1)) 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]); } } } 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&0x1)) 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]); } } } int mdecFreeze(gzFile f, int Mode) { char Unused[4096]; gzfreeze(&mdec, sizeof(mdec)); gzfreezel(iq_y); gzfreezel(iq_uv); gzfreezel(Unused); return 0; }