mirror of
https://github.com/gligli/nulldc-360.git
synced 2025-04-02 11:11:56 -04:00
296 lines
No EOL
6.4 KiB
C++
296 lines
No EOL
6.4 KiB
C++
#include "spg.h"
|
|
#include "Renderer_if.h"
|
|
#include "regs.h"
|
|
#include "threadedPvr.h"
|
|
|
|
#include <dc/sh4/sh4_opcode_list.h>
|
|
|
|
//SPG emulation; Scanline/Raster beam registers & interrupts
|
|
//Time to emulate that stuff correctly ;)
|
|
|
|
u32 in_vblank=0;
|
|
u32 clc_pvr_scanline;
|
|
u32 pvr_numscanlines=512;
|
|
u32 prv_cur_scanline=-1;
|
|
u32 vblk_cnt=0;
|
|
|
|
u32 last_fps=0;
|
|
|
|
volatile bool render_end_pending=false;
|
|
u32 render_end_pending_cycles;
|
|
|
|
//54 mhz pixel clock :)
|
|
#define PIXEL_CLOCK (54*1000*1000/2)
|
|
u32 Line_Cycles=0;
|
|
u32 Frame_Cycles=0;
|
|
void CalculateSync()
|
|
{
|
|
//clc_pvr_scanline=0;
|
|
|
|
u32 pixel_clock;
|
|
float scale_x=1,scale_y=1;
|
|
|
|
if (FB_R_CTRL.vclk_div)
|
|
{
|
|
//VGA :)
|
|
pixel_clock=PIXEL_CLOCK;
|
|
}
|
|
else
|
|
{
|
|
//It is half for NTSC/PAL
|
|
pixel_clock=PIXEL_CLOCK/2;
|
|
}
|
|
|
|
//We need to caclulate the pixel clock
|
|
|
|
// u32 sync_cycles=(SPG_LOAD.hcount+1)*(SPG_LOAD.vcount+1);
|
|
pvr_numscanlines=SPG_LOAD.vcount+1;
|
|
|
|
Line_Cycles=(u32)((u64)DCclock*(u64)(SPG_LOAD.hcount+1)/(u64)pixel_clock);
|
|
|
|
if (SPG_CONTROL.interlace)
|
|
{
|
|
//this is a temp hack
|
|
Line_Cycles/=2;
|
|
// u32 interl_mode=VO_CONTROL.field_mode;
|
|
|
|
//if (interl_mode==2)//3 will be funny =P
|
|
// scale_y=0.5f;//single interlace
|
|
//else
|
|
scale_y=1;
|
|
}
|
|
else
|
|
{
|
|
if (FB_R_CTRL.vclk_div)
|
|
{
|
|
scale_y=1.0f;//non interlaced vga mode has full resolution :)
|
|
}
|
|
else
|
|
scale_y=0.5f;//non interlaced modes have half resolution
|
|
}
|
|
|
|
SetFBScale(scale_x,scale_y);
|
|
|
|
//Frame_Cycles=(u64)DCclock*(u64)sync_cycles/(u64)pixel_clock;
|
|
|
|
Frame_Cycles=pvr_numscanlines*Line_Cycles;
|
|
}
|
|
|
|
extern u32 op_usage[0x10000];
|
|
extern u8* DynarecCache;
|
|
extern u32 DynarecCacheSize;
|
|
|
|
extern u64 time_dr_start;
|
|
extern u64 time_update_system;
|
|
extern u64 time_rw_regs;
|
|
extern u64 time_gdrom;
|
|
extern u64 time_lookup;
|
|
extern u64 time_pref;
|
|
extern u64 time_ta;
|
|
extern u32 gdromaccesses;
|
|
|
|
void PrintBlocksRunCount();
|
|
|
|
void spgVBL()
|
|
{
|
|
u32 curtime=timeGetTime();
|
|
|
|
//Vblank counter
|
|
vblk_cnt++;
|
|
params.RaiseInterrupt(holly_HBLank);// -> This turned out to be HBlank btw , needs to be emulated ;(
|
|
//TODO : rend_if_VBlank();
|
|
VBlank();//notify for vblank :)
|
|
UpdateRRect();
|
|
if ((curtime-last_fps)>500)
|
|
{
|
|
double spd_fps=(double)(FrameCount)/(double)((double)(curtime-(double)last_fps)/1000);
|
|
double spd_vbs=(double)(vblk_cnt)/(double)((double)(curtime-(double)last_fps)/1000);
|
|
double spd_cpu=spd_vbs*Frame_Cycles;
|
|
spd_cpu/=1000000; //mrhz kthx
|
|
double fullvbs=(spd_vbs/spd_cpu)*200;
|
|
|
|
// wchar fpsStr[256];
|
|
const char* mode=0;
|
|
const char* res=0;
|
|
|
|
res=SPG_CONTROL.interlace?"480i":"240p";
|
|
|
|
if (SPG_CONTROL.NTSC==0 && SPG_CONTROL.PAL==1)
|
|
mode="PAL";
|
|
else if (SPG_CONTROL.NTSC==1 && SPG_CONTROL.PAL==0)
|
|
mode="NTSC";
|
|
else
|
|
{
|
|
res=SPG_CONTROL.interlace?"480i":"480p";
|
|
mode="VGA";
|
|
}
|
|
|
|
if(kbhit())
|
|
{
|
|
u32 i,j;
|
|
u32 cumulcnt[0x200]={0};
|
|
switch(getch())
|
|
{
|
|
case 'x':
|
|
threaded_term();
|
|
exit(0);
|
|
break;
|
|
case 'o':
|
|
printf("---- IFB op usage\n");
|
|
|
|
for(i=0;i<0x10000;++i)
|
|
{
|
|
if (op_usage[i])
|
|
{
|
|
sh4_opcodelistentry* opentry=OpDesc[i];
|
|
|
|
for(j=0;j<sizeof(opcodes)/sizeof(sh4_opcodelistentry);++j)
|
|
{
|
|
if (opentry==&opcodes[j])
|
|
{
|
|
cumulcnt[j]+=op_usage[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for(i=0;i<sizeof(opcodes)/sizeof(sh4_opcodelistentry);++i)
|
|
{
|
|
if (cumulcnt[i])
|
|
{
|
|
printf("%12d\t%04x\t%s\n",cumulcnt[i],opcodes[i].rez,opcodes[i].disasm1);
|
|
}
|
|
}
|
|
break;
|
|
case 's':
|
|
printf("---- shil op usage\n");
|
|
|
|
for(i=0;i<0x10000;++i)
|
|
{
|
|
if (op_usage[i])
|
|
{
|
|
printf("%12d\t%4d\n",op_usage[i],i);
|
|
}
|
|
}
|
|
break;
|
|
case 'z':
|
|
printf("---- stats reset\n");
|
|
|
|
time_dr_start=mftb();
|
|
time_update_system=0;
|
|
time_rw_regs=0;
|
|
time_gdrom=0;
|
|
time_lookup=0;
|
|
time_pref=0;
|
|
time_ta=0;
|
|
|
|
for(i=0;i<0x10000;++i)
|
|
{
|
|
op_usage[i]=0;
|
|
}
|
|
break;
|
|
case 'd':
|
|
{
|
|
printf("---- dumping dynarec mem pool @%08x\n",DynarecCache);
|
|
FILE * f=fopen("uda:/nulldc-mempool.bin","wb");
|
|
if(f){
|
|
fwrite(DynarecCache,1,DynarecCacheSize,f);
|
|
fclose(f);
|
|
}
|
|
break;
|
|
}
|
|
case 't':
|
|
printf("UpdateSystem: %.3f%%\n",100.0f*(float)time_update_system/(mftb()-time_dr_start));
|
|
printf("pref: %.3f%%\n",100.0f*(float)time_pref/(mftb()-time_dr_start));
|
|
printf("RW regs: %.3f%%\n",100.0f*(float)time_rw_regs/(mftb()-time_dr_start));
|
|
printf("GDROM: %.3f%%\n",100.0f*(float)time_gdrom/(mftb()-time_dr_start));
|
|
printf("lookup: %.3f%%\n",100.0f*(float)time_lookup/(mftb()-time_dr_start));
|
|
printf("ta wait: %.3f%%\n",100.0f*(float)time_ta/(mftb()-time_dr_start));
|
|
break;
|
|
case 'b':
|
|
PrintBlocksRunCount();
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
printf("%4.2f%% - VPS:%4.1f(%s%s%4.2f) RPS:%4.1f Sh4 %4.2fMhz Gdr %dKB/s\n",
|
|
spd_cpu*100/200,spd_vbs,
|
|
mode,res,fullvbs,
|
|
spd_fps,spd_cpu,(u32)((float)gdromaccesses/(curtime-last_fps)));
|
|
|
|
gdromaccesses=0;
|
|
VertexCount=0;
|
|
last_fps=curtime;
|
|
FrameCount=0;
|
|
vblk_cnt=0;
|
|
}
|
|
}
|
|
|
|
//called from sh4 context , should update pvr/ta state and everything else
|
|
void FASTCALL spgUpdatePvr(u32 cycles)
|
|
{
|
|
if (Line_Cycles==0)
|
|
return;
|
|
clc_pvr_scanline += cycles;
|
|
|
|
if (clc_pvr_scanline > Line_Cycles)//60 ~herz = 200 mhz / 60=3333333.333 cycles per screen refresh
|
|
{
|
|
//ok .. here , after much effort , we did one line
|
|
//now , we must check for raster beam interrupts and vblank
|
|
prv_cur_scanline=(prv_cur_scanline+1)%pvr_numscanlines;
|
|
clc_pvr_scanline -= Line_Cycles;
|
|
//Check for scanline interrupts -- really need to test the scanline values
|
|
|
|
if (SPG_VBLANK_INT.vblank_in_interrupt_line_number == prv_cur_scanline)
|
|
params.RaiseInterrupt(holly_SCANINT1);
|
|
|
|
if (SPG_VBLANK_INT.vblank_out_interrupt_line_number == prv_cur_scanline)
|
|
params.RaiseInterrupt(holly_SCANINT2);
|
|
|
|
if (SPG_VBLANK.vstart == prv_cur_scanline)
|
|
in_vblank=1;
|
|
|
|
if (SPG_VBLANK.vbend == prv_cur_scanline)
|
|
in_vblank=0;
|
|
|
|
if (SPG_CONTROL.interlace)
|
|
SPG_STATUS.fieldnum=~SPG_STATUS.fieldnum;
|
|
else
|
|
SPG_STATUS.fieldnum=0;
|
|
|
|
SPG_STATUS.vsync=in_vblank;
|
|
SPG_STATUS.scanline=prv_cur_scanline;
|
|
|
|
//Vblank start -- really need to test the scanline values
|
|
if (prv_cur_scanline==0)
|
|
{
|
|
spgVBL();
|
|
}
|
|
}
|
|
|
|
if (render_end_pending)
|
|
{
|
|
if (render_end_pending_cycles<cycles)
|
|
{
|
|
HandleLocks();
|
|
threaded_call(EndRender);
|
|
}
|
|
render_end_pending_cycles-=cycles;
|
|
}
|
|
}
|
|
|
|
|
|
bool spg_Init()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void spg_Term()
|
|
{
|
|
}
|
|
|
|
void spg_Reset(bool Manual)
|
|
{
|
|
CalculateSync();
|
|
} |