pureikyubu/Docs/RE/vi.asm
ogamespec@gmail.com 7b3118623b
2011-06-29 14:19:07 +00:00

751 lines
18 KiB
NASM

// VI LIBRARY REVERSING
// return first "1" bit entry from u64 value.
// as example : 0x40000000F0000000, returns 1.
// PPC have same instruction for 32-bit values : cntlzw
int cntlzd(u64 val) { ... }
// 0 - Panasonic, 1 - Normal GC
int getEncoderType() { // HARDCODED 1 }
// VI library have "shadow" registers, placed in RAM.
// VIFlush() is used to update real HW VI registers, from the shadows.
// "changed" contains bit mask, where each bit says, what kind of
// VI registers must be updated inside VI interrupt handler.
u64 changed, shdwChanged;
// this selects mode ()
// 0 - for interlace, 1 - for non-interlace ?
// lets say, that if mode = 1 and current field is odd, then
// dont flush shadows (see VISetRegs()).
u32 changeMode, shdwChangeMode;
//
// VI library registers
//
u16 regs[...], shdwRegs[...]; // see VIFlush
// theese ^^^^^^^ registers actually located there : ---------
|
&bss[0] struct: |
|
offset size description <---------------
-------------------------------------
0000 0x76 regs
0078 0x76 shdwRegs
00F0 0x58 HorVer
-------------------------------------
// "pre"- and "post"- VI interrupt callbacks.
// (note, there is one and only one actual VI master interrupt,
// see intrrupt.txt, there is no stand alone "PRE" VI interrupt
// or "POST" VI either)
static VIRetraceCallback PreCB, PostCB;
//
// thanks N for info :)
//
VI interrupt raised
|
|
V
interrupt handler
|
|
V yes
pre CB? -----> callback
| |
| no |
V |
update shadows <--------
|
|
V yes
post CB? -----> callback
| |
| no |
V |
finish <------------
VIRetraceCallback VISetPreRetraceCallback(VIRetraceCallback callback)
{
VIRetraceCallback old = PreCB;
BOOL level = OSDisableInterrupts();
PreCB = callback;
OSRestoreInterrupts(level);
return old;
}
VIRetraceCallback VISetPostRetraceCallback(VIRetraceCallback callback)
{
VIRetraceCallback old = PostCB;
BOOL level = OSDisableInterrupts();
PostCB = callback;
OSRestoreInterrupts(level);
return old;
}
static volatile u32 retraceCount;
static OSThreadQueue retraceQueue;
void VIWaitForRetrace()
{
BOOL level = OSDisableInterrupts();
u32 cnt = retraceCount;
while(retraceCount == cnt)
{
OSSleepThread(&retraceQueue);
}
OSRestoreInterrupts(level);
}
u32 flushFlag;
void VIFlush()
{
BOOL level = OSDisableInterrupts();
int reg;
shdwChangeMode |= changeMode;
changeMode = 0;
shdwChanged |= changed;
do
{
// get next bit
reg = cntlzd(changed);
// prepare shadows
shdwRegs[reg] = regs[reg];
} while(changed <<= 1);
// ask handler to flush shadows.
flushFlag = 1;
OSRestoreInterrupts(level);
}
//
// *** VI INTERRUPT HANDLER ***
//
__OSExceptionHandler __VIRetraceHandler;
void __VIRetraceHandler(context, exceptionContext);
{
u32 mask = 0;
static int dbgCount;
//
// gather all VI interrupts (retrace, scan-line, not yet clear),
// any VI interrupt cause VI master interrupt (in PI).
// currently SDK have support only for retrace interrupt.
//
if((u16)[CC002030] & 0x8000)
{
(u16)[CC002030] &= 0x8000; // clear .... VI interrupt
mask |= 1;
}
if((u16)[CC002034] & 0x8000)
{
(u16)[CC002034] &= 0x8000; // clear .... VI interrupt
mask |= 2;
}
if((u16)[CC002038] & 0x8000)
{
(u16)[CC002038] &= 0x8000; // clear .... VI interrupt
mask |= 4;
}
if((u16)[CC00203C] & 0x8000)
{
(u16)[CC00203C] &= 0x8000; // clear .... VI interrupt
mask |= 8;
}
// ??? until unknown meaning of registers above.
if(((u16)[CC00203C] & 4) || ((u16)[CC00203C] & 8))
{
OSSetCurrentContext(exceptionContext);
return;
}
// this will be fired, when VI master interrupt raised,
// but other VI interrupts didnt.
// i.e. should never happen.
ASSERT(mask == 0);
// increase vsync counter
retraceCount++;
OSClearContext(sp+16);
OSSetCurrentContext(sp+16);
// PRE CALLBACK
if(PreCB) PreCB(retraceCount);
if(flushFlag)
{
dbgCount = 0;
if(VISetRegs())
{
flushFlag = 0;
__PADRefreshSamplingRate();
}
}
else
{
//
// this happens, when developers dont want to read carefully VI
// documentation. since, its only fired, when mode changed, its
// not critical error :)
//
if(changed)
{
if(dbgCount++ == 60)
{
OSReport("Warning: VIFlush() was not called for 60 frames although VI settings were changed");
}
}
}
OSClearContext(sp+16);
// POST CALLBACK
if(PostCB) PostCB(retraceCount);
// finish VIWaitForRetrace()
OSWakeupThread(retraceQueue);
OSClearContext(sp+16);
OSSetCurrentContext(exceptionContext);
}
// >>>>>> VI ACCESSED ONLY HERE <<<<<<
// and also in VIInit(), of cause.
VITiming *CurrTiming;
u32 CurrTvMode;
int VISetRegs()
{
int regIndex;
// check mode
if(shdwChangeMode == 1)
{
if(getCurrentFieldEvenOdd() == 0) return 0;
}
while(shdwChanged)
{
regIndex = cntlzd(shdwChanged);
(u16)[CC002000][regIndex] = (u16)shdwRegs[regIndex];
shdwChanged <<= 1;
}
shdwChangeMode = 0;
CurrTiming = (u32)[bss + 0x0144];
CurrTvMode = (u32)[bss + 0x0118];
return 1;
}
u16 taps[] = {
01f0 01dc
01ae 0174
0129 00db
008e 0046
000c 00e2
00cb 00c0
00c4 00cf
00de 00ec
00fc 0008
000f 0013
0013 000f
000c 0008
0001 0000
};
void VIInit()
{
encoderType = getEncoderType(); // always 1.
if(((u16)[CC002002] & 1) == 0)
{
__VIInit(0);
}
retraceCount = 0;
changed = shdwChanged = 0;
changeMode = shdwChangeMode = 0;
flushFlag = 0;
//
// antialiasing stuff
//
(u16)[CC00204C] = (taps[1] >> 6) | (taps[2] << 4);
(u16)[CC00204E] = taps[0] | (taps[1] << 10);
(u16)[CC002050] = (taps[4] >> 6) | (taps[5] << 4);
(u16)[CC002052] = taps[3] | (taps[4] << 10);
(u16)[CC002054] = (taps[7] >> 6) | (taps[8] << 4);
(u16)[CC002056] = taps[6] | (taps[7] << 10);
(u16)[CC002058] = taps[11] | (taps[12] << 8);
(u16)[CC00205A] = taps[ 9] | (taps[10] << 8);
(u16)[CC00205C] = taps[15] | (taps[16] << 8);
(u16)[CC00205E] = taps[13] | (taps[14] << 8);
(u16)[CC002060] = taps[19] | (taps[20] << 8);
(u16)[CC002062] = taps[17] | (taps[18] << 8);
(u16)[CC002064] = taps[23] | (taps[24] << 8);
(u16)[CC002066] = taps[21] | (taps[22] << 8);
(u16)[CC002070] = 640;
ImportAdjustingValues(); // WOW! now we know more SRAM variables!
//
// Init VI control block structure (bss[0])
//
vi.HorVer.nonInter = ((u16)[CC002002] >> 1) & 1;
vi.HorVer.tv = ((u16)[CC002002] >> 6) & 3;
vi.HorVer.timing = getTiming(
vi.HorVer.nonInter +
(vi.HorVer.tv == VI_DEBUG) ? (0) : (vi.HorVer.tv << 2);
vi.regs[1] = (u16)[CC002002];
CurrTiming = vi.HorVer.timing;
CurrTvMode = vi.HorVer.tv;
(u16)[bss + 0x00F4] = 640;
(u16)[bss + 0x00F6] = (u16)CurrTiming[1] * 2;
(u16)[bss + 0x00F0] =
bcc: a0 1e 00 f4 lhz r0,244(r30)
bd0: 20 00 02 d0 subfic r0,r0,720
bd4: 7c 03 0e 70 srawi r3,r0,1
bd8: 7c 63 01 94 addze r3,r3
bdc: b0 7e 00 f0 sth r3,240(r30)
(u16)[bss + 0x00F2] = 0;
AdjustPosition((u16)CurrTiming[1]);
(u16)[bss + 0x0102] = 640;
(u16)[bss + 0x0104] = (u16)CurrTiming[1] * 2;
(u16)[bss + 0x0106] = 0;
(u16)[bss + 0x0108] = 0;
(u16)[bss + 0x010A] = 640;
(u16)[bss + 0x010C] = (u16)CurrTiming[1] * 2;
(u32)[bss + 0x0110] = 0;
( u8)[bss + 0x011C] = 0x28;
( u8)[bss + 0x011D] = 0x28;
( u8)[bss + 0x011E] = 0x28;
( u8)[bss + 0x012C] = 0;
(u32)[bss + 0x0130] = 1;
(u32)[bss + 0x0134] = 0;
//
// install VI retrace handler
//
OSInitThreadQueue(&retraceQueue);
(u16)[CC002030] &= 0x8000;
(u16)[CC002034] &= 0x8000;
PreCB = PostCB = 0;
__OSSetInterruptHandler(VI, __VIRetraceHandler);
__OSUnmaskInterrupts(VI);
}
s16/u16 displayOffsetH, displayOffsetV;
void ImportAdjustingValues()
{
OSSram *sram = __OSLockSram();
// error, if sram == NULL
ASSERT(sram);
displayOffsetH = *(s8 *)(sram + 0x10);
displayOffsetV = 0;
__OSUnlockSram(0);
}
void __VIInit(VITVMode mode)
{
VITiming *tm;
int nonInter, tv, a;
int encoderType = getEncoderType();
u16 hct, vct;
if(encoderType == 0)
{
__VIInitPhilips();
}
nonInter = mode & 2;
tv = mode >> 2;
OSTvMode = tv; // [800000CC]
if(encoderType == 0)
{
tv = VI_DEBUG;
}
tm = getTiming(mode);
//
// wait a little
//
*(u16 *)(0xCC002002) = 2;
for(a=0; a<1000; a++) ;
*(u16 *)(0xCC002002) = 0;
//
// setup timing registers
//
*(u16 *)(0xCC002006) = tm->hlw;
*(u16 *)(0xCC002004) = (tm->hcs << 8) | tm->hce;
*(u16 *)(0xCC00200A) = (tm->hbe640 << 7) | tm->hsy;
*(u16 *)(0xCC002008) = (tm->hbe640 >> 9) | (tm->hbs640 << 1);
if(encoderType == 0)
{
*(u16 *)(0xCC002072) = (tm->hbeCCIR656 | 0x8000);
*(u16 *)(0xCC002074) = tm->hbsCCIR656;
}
*(u16 *)(0xCC002000) = tm->equ;
*(u16 *)(0xCC00200E) = tm->prbOdd + tm->acv * 2 - 2;
*(u16 *)(0xCC00200C) = tm->psbOdd + 2;
*(u16 *)(0xCC002012) = tm->prbEven + tm->acv * 2 - 2;
*(u16 *)(0xCC002010) = tm->psbEven + 2;
*(u16 *)(0xCC002016) = (tm->be1 << 5) | tm->bs1;
*(u16 *)(0xCC002014) = (tm->be3 << 5) | tm->bs3;
*(u16 *)(0xCC00201A) = (tm->be2 << 5) | tm->bs2;
*(u16 *)(0xCC002018) = (tm->be4 << 5) | tm->bs4;
*(u16 *)(0xCC002048) = 0x2828;
*(u16 *)(0xCC002036) = 1;
*(u16 *)(0xCC002034) = 0x1001;
hct = tm->hlw + 1;
vct = tm->nhlines * 2 + 1;
*(u16 *)(0xCC002032) = hct;
*(u16 *)(0xCC002030) = vct | 0x1000;
if((mode != 2) || (mode != 3))
{
*(u16 *)(0xCC002002) = (tv << 8) | (nonInter << 2) | 1;
*(u16 *)(0xCC00206C) = 0;
}
else
{
*(u16 *)(0xCC002002) = (tv << 8) | 5;
*(u16 *)(0xCC00206C) = 1;
}
}
//
// predefined "Timing" registers for different modes.
//
static u8 timing[] = {
0x06,0x00,0x00,0xf0,0x00,0x18,0x00,0x19, // +0
0x00,0x03,0x00,0x02,0x0c,0x0d,0x0c,0x0d,
0x02,0x08,0x02,0x07,0x02,0x08,0x02,0x07,
0x02,0x0d,0x01,0xad,0x40,0x47,0x69,0xa2,
0x01,0x75,0x7a,0x00,
0x01,0x9c,0x06,0x00,0x00,0xf0,0x00,0x18, // +38
0x00,0x18,0x00,0x04,0x00,0x04,0x0c,0x0c,
0x0c,0x0c,0x02,0x08,0x02,0x08,0x02,0x08,
0x02,0x08,0x02,0x0e,0x01,0xad,0x40,0x47,
0x69,0xa2,0x01,0x75,
0x7a,0x00,0x01,0x9c,0x05,0x00,0x01,0x1f, // +76
0x00,0x23,0x00,0x24,0x00,0x01,0x00,0x00,
0x0d,0x0c,0x0b,0x0a,0x02,0x6b,0x02,0x6a,
0x02,0x69,0x02,0x6c,0x02,0x71,0x01,0xb0,
0x40,0x4b,0x6a,0xac,
0x01,0x7c,0x85,0x00,0x01,0xa4,0x05,0x00, // +114
0x01,0x1f,0x00,0x21,0x00,0x21,0x00,0x02,
0x00,0x02,0x0d,0x0b,0x0d,0x0b,0x02,0x6b,
0x02,0x6d,0x02,0x6b,0x02,0x6d,0x02,0x70,
0x01,0xb0,0x40,0x4b,
0x6a,0xac,0x01,0x7c,0x85,0x00,0x01,0xa4, // +152
0x06,0x00,0x00,0xf0,0x00,0x18,0x00,0x19,
0x00,0x03,0x00,0x02,0x10,0x0f,0x0e,0x0d,
0x02,0x06,0x02,0x05,0x02,0x04,0x02,0x07,
0x02,0x0d,0x01,0xad,
0x40,0x4e,0x70,0xa2,0x01,0x75,0x7a,0x00, // +190
0x01,0x9c,0x06,0x00,0x00,0xf0,0x00,0x18,
0x00,0x18,0x00,0x04,0x00,0x04,0x10,0x0e,
0x10,0x0e,0x02,0x06,0x02,0x08,0x02,0x06,
0x02,0x08,0x02,0x0e,
0x01,0xad,0x40,0x4e,0x70,0xa2,0x01,0x75, // +228
0x7a,0x00,0x01,0x9c,0x0c,0x00,0x01,0xe0,
0x00,0x30,0x00,0x30,0x00,0x06,0x00,0x06,
0x18,0x18,0x18,0x18,0x04,0x0e,0x04,0x0e,
0x04,0x0e,0x04,0x0e,
0x04,0x1a,0x01,0xad,0x40,0x47,0x69,0xa2, // +266
0x01,0x75,0x7a,0x00,0x01,0x9c,0x0c,0x00,
0x01,0xe0,0x00,0x2c,0x00,0x2c,0x00,0x0a,
0x00,0x0a,0x18,0x18,0x18,0x18,0x04,0x0e,
0x04,0x0e,0x04,0x0e,0x04,0x0e,0x04,0x1a,
0x01,0xad,0x40,0x47,0x69,0xa8,0x01,0x7b,
0x7a,0x00,0x01,0x9c
};
// return timing registers.
void *getTiming(VITVMode mode)
{
u8 *ptr;
ptr = timing;
switch(mode)
{
//
// normal modes
//
case VI_TVMODE_NTSC_INT: return ptr;
case VI_TVMODE_NTSC_DS: return &ptr[38];
case VI_TVMODE_PAL_INT: return &ptr[76];
case VI_TVMODE_PAL_DS: return &ptr[114];
case VI_TVMODE_EURGB60_INT: return ptr;
case VI_TVMODE_EURGB60_DS: return &ptr[38];
case VI_TVMODE_MPAL_INT: return &ptr[152];
case VI_TVMODE_MPAL_DS: return &ptr[190];
//
// specific modes
//
case VI_TVMODE_NTSC_PROG: return &ptr[228];
case 3 <<-- WTF Panasonic ??: return &ptr[266];
case VI_TVMODE_DEBUG_PAL_INT: return &ptr[76];
case VI_TVMODE_DEBUG_PAL_DS: return &ptr[114];
default:
break;
}
return 0;
}
Render Mode :
-------------
u32 viTVmode; +0
INTERLACE = 0
NON-INTERLACE = 1
PROGRESSIVE = 2
u16 fbWidth; +4
u16 efbHeight; +6
u16 xfbHeight; +8
u16 viXOrigin; +10
u16 viYOrigin; +12
u16 viWidth; +14
u16 viHeight; +16
u32 xFBmode; +20
XFB-SF = 0
XFB-DF = 1
u8 field_rendering; +
u8 aa; +
u8 sample_pattern[12][2]; +
u8 vfilter[7]; +
static BOOL FBSet;
void VISetNextFrameBuffer(void *fb)
{
BOOL level;
if(fb & 0x1f)
{
OSPanic("VISetNextFrameBuffer(): Frame buffer address(0x%08x) is not 32byte aligned", fb);
}
level = OSDisableInterrupts();
vi.HorVer.bufAddr = fb;
FBSet = 1;
setFbbRegs(
&vi.HorVer,
&vi.HorVer.tfbb,
&vi.HorVer.bfbb,
&vi.HorVer.rtfbb,
&vi.HorVer.rbfbb,
);
OSRestoreInterrupts(level);
}
void setFbbRegs(VIHorVer *HorVer, void **tfbb, void **bfbb, void **rtfbb, void **rbfbb)
{
int shifted;
calcFbbs(
HorVer->bufAddr,
HorVer->PanPosX,
HorVer->AdjustedPanPosY,
HorVer->wordPerLine,
HorVer->FBMode,
HorVer->AdjustedDispPosY,
tfbb,
bfbb
);
if(HorVer->threeD)
{
calcFbbs(
HorVer->rbufAddr,
HorVer->PanPosX,
HorVer->AdjustedPanPosY,
HorVer->wordPerLine,
HorVer->FBMode,
HorVer->AdjustedDispPosY,
rtfbb,
rbfbb
);
}
if( (*tfbb < 0x01000000) &&
(*bfbb < 0x01000000) &&
(*rtfbb < 0x01000000) &&
(*rbfbb < 0x01000000) )
{
shifted = 0;
}
else shifted = 1;
if(shifted)
{
*tfbb >>= 5;
*bfbb >>= 5;
*rtfbb >>= 5;
*rbfbb >>= 5;
}
vi.regs[14] = (*tfbb >> 16) | (HorVer->xof << 8) | (shifted << 12);
changed |= bit14;
vi.regs[15] = (*tfbb & 0xffff);
changed |= bit15;
vi.regs[18] = (*bfbb >> 16);
changed |= bit18;
vi.regs[19] = (*bfbb & 0xffff);
changed |= bit19;
if(HorVer->threeD)
{
vi.regs[16] = (*rtfbb >> 16);
changed |= bit16;
vi.regs[17] = (*rtfbb & 0xffff);
changed |= bit17;
vi.regs[20] = (*rbfbb >> 16);
changed |= bit20;
vi.regs[21] = (*rbfbb & 0xffff);
changed |= bit21;
}
}
void calcFbbs(
void *bufAddr,
u16 panPosX,
u16 panPosY,
u8 wordPerLine,
u32 xfbMode,
s16 dispPosY,
void **tfbb,
void **bfbb)
{
int bytesPerLine, xoffInWords;
void *tmp;
bytesPerLine = wordPerLine * 32;
xoffInWords = panPosX * 2;
*tfbb = bufAddr + (bytesPerLine * panPosY) + xoffInWords;
*bfbb = (xfbMode == VI_XFBMODE_SF) ? (*tfbb) : (*tfbb + bytesPerLine);
if((dispPosY * 2 - dispPosY) == 1)
{
tmp = *bfbb;
*bfbb = *tfbb;
*tfbb = tmp;
}
*tfbb &= 0x3fffffff;
*bfbb &= 0x3fffffff;
}
void setHorizontalRegs(VITiming *tm, u16 dispPosX, u16 dispSizeX)
{
int hbe, hbs, hbeHi, hbeLo;
vi.regs[2] = tm->hcs << 8;
changed |= bit2;
vi.regs[3] = tm->hlw;
changed |= bit3;
hbe = tm->hbe640 + dispPosX - 40;
hbs = (720 - dispSizeX) - (tm->hbs640 + dispPosX + 40);
hbeHi = (hbe & 0x1ff) << 9;
hbeLo = hbe >> 9;
vi.regs[4] = hbeLo | (hbs * 2);
changed |= bit4;
vi.regs[5] = hbeHi | tm->hsy;
changed |= bit5;
}
void setInterruptRegs(VITiming *tm)
{
u16 vct = (tm->nhlines >> 1) + 1;
u16 hct = ((tm->nhlines >> 1) << 1) - (tm->nhlines);
int borrow = (hct) ? (0) : (tm->hlw) + 1;
vi.regs[24] = vct | 0x1000;
changed |= bit24;
vi.regs[25] = borrow;
changed |= bit25;
}