unit dev_dce; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses sysutils, bittype, vm, vmparam, sys_conf, sys_event, time, kern_mtx, sys_bootparam, display_interface; procedure dce_initialize(); function TriggerFlipEop(submit_id:QWORD):Integer; type p_dce_page=^t_dce_page; t_dce_page=packed record //0x170 zero :array[0..19] of QWORD; labels :array[0..15] of QWORD; //sceVideoOutGetBufferLabelAddress label_ :QWORD; unk2 :QWORD; ident_flip :QWORD; ident_vblank :QWORD; filter_id :Integer; _align :Integer; vopen_counter:QWORD; ident_setmode:QWORD; unk3 :array[0..23] of Byte; end; {$IF sizeof(t_dce_page)<>$170}{$STOP sizeof(t_dce_page)<>$170}{$ENDIF} var dce_interface:TAbstractDisplay=TDisplayHandle; dce_handle:TDisplayHandle; dce_mtx:mtx; dce_page:p_dce_page; knlist_lock_flip:mtx; g_video_out_event_flip:t_knlist; implementation uses errno, systm, md_time, vm_pmap, subr_backtrace, sys_vm_object, vm_pager, kern_proc, kern_timeout; const EVENTID_FLIP =$0006; EVENTID_VBLANK =$0007; EVENTID_SETMODE =$0051; EVENTID_POSITION =$0058; EVENTID_PREVBLANK=$0059; //$0061 sys_update_scaler //$0062 sys_prevblank wZI4fmUJMlw / vXMpe7Murfc //$0063 sys_vblank procedure knote_eventid(event_id:WORD;flipArg:QWORD;lockflags:Integer); begin knote(@g_video_out_event_flip, event_id or (flipArg shl 16), lockflags); end; var vblank:record lock :mtx; callout:t_callout; refs :Int64; count :QWORD; ptime :QWORD; tsc :QWORD; fps_cnt:QWORD; fps_tsc:QWORD; end; procedure vblank_expire(arg:Pointer); var i:QWORD; trigger:Boolean; begin if (vblank.refs<>0) then begin knote_eventid(EVENTID_PREVBLANK, vblank.count, 0); //SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START //mtx_lock(dce_mtx); trigger:=False; if (dce_handle<>nil) then begin trigger:=dce_handle.Vblank(); end; trigger:=True; if trigger then begin vblank.ptime:=GetProcessTime; vblank.tsc :=rdtsc(); i:=System.InterlockedIncrement64(vblank.count)-1; end; //mtx_unlock(dce_mtx); // callout_reset(@vblank.callout, vblank.callout.c_time, @vblank_expire, nil); // if trigger then begin knote_eventid(EVENTID_VBLANK , i, 0); //SCE_VIDEO_OUT_EVENT_VBLANK end; if (vblank.fps_tsc=0) then begin vblank.fps_tsc:=rdtsc(); vblank.fps_cnt:=0; end else begin //Inc(fps_cnt); if ((rdtsc()-vblank.fps_tsc) div tsc_freq)>=1 then begin p_host_ipc.SetCaptionFPS(vblank.fps_cnt); vblank.fps_cnt:=0; vblank.fps_tsc:=rdtsc(); end; end; end; end; procedure open_vblank; var time:Int64; begin if (System.InterlockedIncrement64(vblank.refs)=1) then begin time:=round(hz/59.94); callout_reset(@vblank.callout, time, @vblank_expire, nil); end; end; procedure close_vblank; begin System.InterlockedDecrement64(vblank.refs); end; type p_flip_control_args=^t_flip_control_args; t_flip_control_args=packed record id :DWORD; align:DWORD; arg2 :QWORD; arg3 :QWORD; arg4 :QWORD; arg5 :QWORD; arg6 :QWORD; end; p_resolution_status=^t_resolution_status; t_resolution_status=packed record width :DWORD; heigth :DWORD; paneWidth :DWORD; paneHeight :DWORD; refreshHz :DWORD; //Single screenSizeInInch:DWORD; //Single padding:array[0..19] of Byte; end; t_scaler_info=array[0..63] of Byte; //SCE_VIDEO_OUT_REFRESH_RATE_UNKNOWN = 0, //SCE_VIDEO_OUT_REFRESH_RATE_23_98HZ = 1, //SCE_VIDEO_OUT_REFRESH_RATE_50HZ = 2, //SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ = 3, //SCE_VIDEO_OUT_REFRESH_RATE_29_97HZ = 6, //SCE_VIDEO_OUT_REFRESH_RATE_89_91HZ = 35, 0x23 //SCE_VIDEO_OUT_REFRESH_RATE_119_88HZ = 13, 0xD //refreshRate = 0 SCE_VIDEO_OUT_REFRESH_RATE_UNKNOWN //refreshRate = 3; result.refreshHz = 0x426fc28f( 59.94) SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ //refreshRate = 2, result.refreshHz = 0x42480000( 50.00) SCE_VIDEO_OUT_REFRESH_RATE_50HZ //refreshRate = 1, result.refreshHz = 0x41bfd70a( 23.98) SCE_VIDEO_OUT_REFRESH_RATE_23_98HZ //refreshRate = 4, result.refreshHz = 0x41c00000( 24.00) //refreshRate = 5, result.refreshHz = 0x41f00000( 30.00) //refreshRate = 6, result.refreshHz = 0x41efc28f( 29.97) SCE_VIDEO_OUT_REFRESH_RATE_29_97HZ //refreshRate = 7, result.refreshHz = 0x41c80000( 25.00) //refreshRate = 9, result.refreshHz = 0x42700000( 60.00) //refreshRate = 10, result.refreshHz = 0x42400000( 48.00) //refreshRate = 0xb, result.refreshHz = 0x423fcccd( 47.95) //refreshRate = 0xc, result.refreshHz = 0x42c80000(100.00) //refreshRate = 0xd, result.refreshHz = 0x42efc28f(119.88) SCE_VIDEO_OUT_REFRESH_RATE_119_88HZ //refreshRate = 0xe, result.refreshHz = 0x42f00000(120.00) //refreshRate = 0xf, result.refreshHz = 0x43480000(200.00) //refreshRate = 0x10, result.refreshHz = 0x436fc28f(239.76) //refreshRate = 0x11, result.refreshHz = 0x43700000(240.00) //refreshRate = 0x14, result.refreshHz = 0x413fd70a( 11.99) //refreshRate = 0x15, result.refreshHz = 0x41400000( 12.00) //refreshRate = 0x16, result.refreshHz = 0x416fd70a( 14.99) //refreshRate = 0x17, result.refreshHz = 0x41700000( 15.00) //refreshRate = 0x23, result.refreshHz = 0x42b3d1ec( 89.91) SCE_VIDEO_OUT_REFRESH_RATE_89_91HZ type p_flip_status=^t_flip_status; t_flip_status=packed record flipArg :QWORD; flipArg2 :QWORD; count :QWORD; processTime :QWORD; tsc :QWORD; currentBuffer :DWORD; flipPendingNum0:DWORD; gcQueueNum :DWORD; flipPendingNum1:DWORD; submitTsc :QWORD; f_0x40 :QWORD; end; p_vblank_args=^t_vblank_args; t_vblank_args=packed record count :QWORD; processTime:QWORD; tsc :QWORD; flags :Byte; _align :array[0..6] of Byte; reserved :QWORD; end; pSceVideoOutColorSettings=^SceVideoOutColorSettings; SceVideoOutColorSettings=packed record gamma :array[0..2] of Single; option:DWORD; end; type p_cursor_enable=^t_cursor_enable; t_cursor_enable=packed record rtype :DWORD; //0 index :DWORD; //Cursor index (0-1) args :DWORD; //3 = enable; 1 = disable enable :DWORD; //1 = enable; 0 = disable addr_hi:DWORD; addr_lo:DWORD; padding:array[0..13] of DWORD; end; p_cursor_img_addr=^t_cursor_img_addr; t_cursor_img_addr=packed record rtype :DWORD; //1 index :DWORD; //Cursor index (0-1) args :DWORD; //2 addr_hi:DWORD; addr_lo:DWORD; padding:array[0..14] of DWORD; end; p_cursor_pos=^t_cursor_pos; t_cursor_pos=packed record rtype :DWORD; //2 index :DWORD; //Cursor index (0-1) args :DWORD; //2 posX :DWORD; posY :DWORD; padding:array[0..14] of DWORD; end; p_cursor_pos_stereo=^t_cursor_pos_stereo; t_cursor_pos_stereo=packed record rtype :DWORD; //3 index :DWORD; //Cursor index (0-1) args :DWORD; //3 posX :DWORD; posY :DWORD; offset :DWORD; padding:array[0..13] of DWORD; end; p_cursor_hot_spot=^t_cursor_hot_spot; t_cursor_hot_spot=packed record rtype :DWORD; //4 index :DWORD; //Cursor index (0-1) args :DWORD; //2 hotX :DWORD; hotY :DWORD; padding:array[0..14] of DWORD; end; p_cursor_magnify=^t_cursor_magnify; t_cursor_magnify=packed record rtype :DWORD; //5 index :DWORD; //Cursor index (0-1) args :DWORD; //1 enable :DWORD; padding:array[0..15] of DWORD; end; p_cursor_update_pending=^t_cursor_update_pending; t_cursor_update_pending=packed record rtype :DWORD; //6 index :DWORD; //Cursor index (0-1) args :DWORD; //1 ptype :DWORD; padding:array[0..14] of DWORD; result :DWORD; //0,1 end; Function dce_set_cursor_info(dev:p_cdev;canary:QWORD;arg5:DWORD;data:Pointer):Integer; var info:t_cursor_enable; addr:qword; begin info:=Default(t_cursor_enable); Result:=copyin(data,@info,80); if (Result<>0) then Exit; case info.rtype of 0: //enable/disable begin case info.enable of 1: begin addr:=info.addr_lo or (QWORD(info.addr_hi) shl 32); Writeln('dce_set_cursor_enable:',canary,' ', info.index,' ', HexStr(addr,16)); end; 0: begin Writeln('dce_set_cursor_disable:',canary,' ', info.index); end; end; end; 1: //SetImageAddress begin addr:=p_cursor_img_addr(@info)^.addr_lo or (QWORD(p_cursor_img_addr(@info)^.addr_hi) shl 32); Writeln('dce_set_image_addr:',canary,' ', p_cursor_img_addr(@info)^.index,' ', HexStr(addr,16)); end; 2: //SetPosition begin Writeln('dce_set_cursor_pos:',canary,' ', p_cursor_pos(@info)^.index,' ', p_cursor_pos(@info)^.posX,' ', p_cursor_pos(@info)^.posY); end; 3:; //SetPositionStereo 4:; //SetHotSpot 5:; //Set2xMagnify 6: //IsUpdatePending begin Writeln('dce_is_update_pending:',canary,' ', p_cursor_update_pending(@info)^.index,' ', p_cursor_update_pending(@info)^.ptype); addr:=1; Result:=copyout(@addr,@p_cursor_update_pending(data)^.result,8); end; else Exit(EINVAL); end; end; var f_vopen_counter:QWORD=0; f_eop_count:Integer=1; Function dce_flip_control(dev:p_cdev;data:p_flip_control_args):Integer; var ptr :Pointer; poff:PQWORD; plen:PQWORD; len:QWORD; u:record case byte of 0:(r_status:t_resolution_status); 1:(f_status:t_flip_status); 2:(i_scaler:t_scaler_info); 3:(v_vblank:t_vblank_args); 4:(color :SceVideoOutColorSettings) end; begin Result:=0; //Writeln('dce_flip_control(',data^.id,')'); //id -> 0..0x24 case data^.id of 0: //video open begin if (data^.arg6=0) and (data^.arg2=0) then begin //arg3 [0..1] case Integer(data^.arg3) of 0:; 1:begin //sceSblACMgrIsSystemUcred Exit(EPERM); end; else begin Exit(EINVAL); end; end; mtx_lock(dce_mtx); if (dce_handle=nil) then begin dce_handle:=dce_interface.Create; if (dce_handle=nil) then begin Result:=EBUSY; end else begin dce_handle.event_flip:=@g_video_out_event_flip; dce_handle.labels :=@dce_page^.labels; dce_handle.dce_mtx :=@dce_mtx; dce_handle.p_fps_cnt :=@vblank.fps_cnt; Result:=dce_handle.Open(); end; end else begin Result:=EBUSY; end; if (Result=0) then begin f_vopen_counter:=f_vopen_counter+1; FillChar(dce_page^,SizeOf(t_dce_page),0); dce_page^.filter_id:=-13; dce_page^.ident_flip :=QWORD($06000000000000); dce_page^.ident_vblank :=QWORD($07000000000000); dce_page^.ident_setmode:=QWORD($51000000000000); dce_page^.vopen_counter:=f_vopen_counter; f_eop_count:=1; end; mtx_unlock(dce_mtx); if (Result<>0) then Exit; Writeln('dce_video_open'); ptr:=Pointer(data^.arg5); len:=$a5a5; //canary copyout(@len,ptr,SizeOf(QWORD)); open_vblank; Exit(0); end; Exit(EINVAL); end; 1: //video close begin if (data^.arg3=0) and (data^.arg4=0) and (data^.arg5=0) and (data^.arg6=0) then begin //arg2 -> canary if (data^.arg2<>$a5a5) then Exit(EINVAL); mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin FreeAndNil(dce_handle); end; if (Result=0) then begin FillChar(dce_page^,SizeOf(t_dce_page),0); end; mtx_unlock(dce_mtx); if (Result<>0) then Exit; Writeln('dce_video_close:',data^.arg2); close_vblank; kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($06000000000000)); kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($07000000000000)); kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($51000000000000)); kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($58000000000000)); kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($59000000000000)); Exit(0); end; Exit(EINVAL); end; 4: //UnregisterBuffer begin if (data^.arg4=0) and (data^.arg5=0) and (data^.arg6=0) then begin //arg2 -> canary //arg3 -> buffer id if (data^.arg2<>$a5a5) then Exit(EINVAL); if (DWORD(data^.arg3)>15) then Exit(EINVAL); mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin Result:=dce_handle.UnregisterBuffer(Integer(data^.arg3)); end; mtx_unlock(dce_mtx); if (Result<>0) then Exit; Writeln('UnregisterBuffer:',data^.arg2,' ',data^.arg3); Exit(0); end; Exit(EINVAL); end; 5: //UnregisterBufferAttribute begin if (data^.arg4=0) and (data^.arg5=0) and (data^.arg6=0) then begin //arg2 -> canary //arg3 -> attr id if (data^.arg2<>$a5a5) then Exit(EINVAL); if (DWORD(data^.arg3)>3) then Exit(EINVAL); mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin Result:=dce_handle.UnregisterBufferAttribute(Integer(data^.arg3)); end; mtx_unlock(dce_mtx); if (Result<>0) then Exit; Writeln('UnregisterBufferAttribute:',data^.arg2,' ',data^.arg3); Exit(0); end; Exit(EINVAL); end; 6: //SetFlipRate begin if (data^.arg4=0) and (data^.arg5=0) and (data^.arg6=0) then begin //arg2 -> canary //arg3 -> rate [0..7] if (data^.arg2<>$a5a5) then Exit(EINVAL); if (DWORD(data^.arg3)>7) then Exit(EINVAL); mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin Result:=dce_handle.SetFlipRate(Integer(data^.arg3)); end; mtx_unlock(dce_mtx); if (Result<>0) then Exit; Writeln('SetFlipRate:',data^.arg3); Exit(0); end; Exit(EINVAL); end; 7: //sceVideoOutSetWindowModeMargins begin if (data^.arg5=0) and (data^.arg6=0) then begin //arg2 -> canary //arg3 -> top //arg4 -> bottom if (DWORD(data^.arg3) > $fff) or (DWORD(data^.arg4) > $fff) then begin Exit(EINVAL); end; Writeln('sceVideoOutSetWindowModeMargins:',DWORD(data^.arg3),' ',DWORD(data^.arg4)); Exit(0); end; Exit(EINVAL); end; 9: begin //get page info if (data^.arg5=0) and (data^.arg6=0) then begin //arg2 -> canary if (data^.arg2<>$a5a5) then Exit(EINVAL); poff:=Pointer(data^.arg3); //output offset //0..3FFC000 plen:=Pointer(data^.arg4); //output len //4000 len:=$4000; Result:=copyout(@len,plen,SizeOf(QWORD)); if (Result=0) then begin len:=0; Result:=copyout(@len,poff,SizeOf(QWORD)); end; Exit; end; Exit(EINVAL); end; 10: //sceVideoOutGetFlipStatus begin if (data^.arg6=0) and (Integer(data^.arg4)>0) then begin //arg2 -> canary //arg3 = &result; //arg4 = 72 if (data^.arg2<>$a5a5) then Exit(EINVAL); ptr:=Pointer(data^.arg3); len:=DWORD(data^.arg4); if (len>SizeOf(t_flip_status)) then begin len:=SizeOf(t_flip_status); end; u.f_status:=Default(t_flip_status); mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin Result:=dce_handle.GetFlipStatus(@u.f_status); end; mtx_unlock(dce_mtx); if (Result<>0) then Exit; Result:=copyout(@u.f_status,ptr,len); Exit; end; Exit(EINVAL); end; 11: //sceVideoOutGetVblankStatus begin if (data^.arg5=0) and (data^.arg6=0) and (Integer(data^.arg4)>0) then begin //arg2 -> canary //arg3 = &result; //arg4 = 40 //Writeln('sceVideoOutGetVblankStatus'); if (data^.arg2<>$a5a5) then Exit(EINVAL); ptr:=Pointer(data^.arg3); len:=DWORD(data^.arg4); if (len>SizeOf(t_vblank_args)) then begin len:=SizeOf(t_vblank_args); end; u.v_vblank:=Default(t_vblank_args); mtx_lock(vblank.lock); u.v_vblank.count :=vblank.count; u.v_vblank.processTime:=vblank.ptime; u.v_vblank.tsc :=vblank.tsc; mtx_unlock(vblank.lock); Result:=copyout(@u.v_vblank,ptr,len); Exit; end; Exit(EINVAL); end; 12: //sceVideoOutSysUpdateScalerParameters begin if (data^.arg5=0) and (data^.arg6=0) then begin if (data^.arg4=$30) or (data^.arg4=$40) then begin //arg2 -> canary //arg3 -> ptr //arg4 -> 64 if (data^.arg2<>$a5a5) then Exit(EINVAL); ptr:=Pointer(data^.arg3); len:=QWORD (data^.arg4); u.i_scaler:=Default(t_scaler_info); //Writeln('dce_flip_control(',data^.id,'):get_data?'); //print_backtrace_td(stderr); Result:=copyout(@u.i_scaler,ptr,len); Exit; end; end; Exit(EINVAL); end; 19: //sceVideoOutGetResolutionStatus begin if (data^.arg4=44) then begin //arg2 -> canary //arg3 = &result; //arg4 = 44; if (data^.arg2<>$a5a5) then Exit(EINVAL); ptr:=Pointer(data^.arg3); len:=QWORD (data^.arg4); u.r_status:=Default(t_resolution_status); mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin Result:=dce_handle.GetResolutionStatus(@u.r_status); end; mtx_unlock(dce_mtx); if (Result<>0) then Exit; Result:=copyout(@u.r_status,ptr,len); Exit; end; Exit(EINVAL); end; 24: //sceVideoOutCursor* begin if (data^.arg6=0) and (data^.arg4=80) then begin //arg2 -> canary //arg4 = 80; if (data^.arg2<>$a5a5) then Exit(EINVAL); Result:=dce_set_cursor_info(dev,data^.arg2,data^.arg5,Pointer(data^.arg3)); Exit; end; Exit(EINVAL); end; 31: //sys begin //arg2 -> canary //arg3 <- subtype 0..13 if (data^.arg2<>$a5a5) then Exit(EINVAL); if (data^.arg3>13) then Exit(EINVAL); case data^.arg3 of 12:Writeln('SetLabelsAddr:',' 0x',HexStr(data^.arg4,10)); else Writeln('dce_flip_control(',data^.id,'):',data^.arg3,' 0x',HexStr(data^.arg4,10)); end; end; 33: //sceVideoOutAdjustColor_ begin //arg2 -> canary //arg3 -> pSetting; //arg4 -> size; if (data^.arg2<>$a5a5) then Exit(EINVAL); ptr:=Pointer(data^.arg3); len:=data^.arg4; if (len<>16) and (len<>12) then Exit(EINVAL); u.color:=Default(SceVideoOutColorSettings); if (len<>12) then begin u.color.option:=$ffffffff; end; Result:=copyin(ptr,@u.color,len); if (Result<>0) then Exit; Writeln('sceVideoOutAdjustColor(',u.color.gamma[0]:0:2,',', u.color.gamma[1]:0:2,',', u.color.gamma[2]:0:2,',0x',HexStr(u.color.option,8)+')'); Exit(0); end; else begin print_error_td('dce_flip_control('+IntToStr(data^.id)+')'); Assert(False); Result:=EINVAL; end; end; end; type p_register_buffer_attr_args=^t_register_buffer_attr_args; t_register_buffer_attr_args=packed record canary :QWORD; //arg5 data in dce_flip_control:0:pointer(arg5)^ attrid :Byte; //attribute id [0..3] submit :Byte; //0 = RegisterBuffers ; 1 = SubmitChangeBufferAttribute f_0xa :WORD; pixelFormat:DWORD; tilingMode :DWORD; pitchPixel :DWORD; width :DWORD; height :DWORD; f_0x20 :Byte; f_0x21 :Byte; options :WORD; reserved1 :QWORD; reserved2 :DWORD; end; t_pixelFormat=bitpacked record unknow_1 :bit8; //[0..7] (always zero) GRPH_BLUE_CROSSBAR :bit2; //[8..9] [BARG] GRPH_GREEN_CROSSBAR:bit2; //[10..11] [GBAR] GRPH_RED_CROSSBAR :bit2; //[12..13] [RGBA] GRPH_ALPHA_CROSSBAR:bit2; //[14..15] [ARGB] COLORSPACE :bit3; //[16..18] [SRGB=0,2,HDR=4,5,NORM=6] unknow_3 :bit1; //[19..19] (always zero) unknow_4 :bit4; //[20..23] [0,2,3,4,5,7,8] IS_FLOAT :bit1; //[24..24] [0,1] unknow_5 :bit2; //[25..26] (always zero) GRPH_FORMAT :bit3; //[27..29] [0,1,4,6,7] GRPH_DEPTH :bit2; //[30..31] [16BPP=1,32BPP=2,3] end; Function dce_register_buffer_attr(dev:p_cdev;data:p_register_buffer_attr_args):Integer; begin Result:=0; if (data^.canary<>$a5a5) then Exit(EINVAL); if (data^.attrid>3) then Exit(EINVAL); if (data^.reserved2<>0) or (data^.f_0x21<>0) or (data^.pitchPixel1) then Exit(EINVAL); if (data^.pitchPixel>$2000) or (data^.width>$2000) or (data^.height>$2000) then Exit(EINVAL); case data^.pixelFormat of $80000000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A8R8G8B8_SRGB $80002200:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A8B8G8R8_SRGB $80060000:; $80062200:; $80220000:; $80320000:; $80420000:; $80520000:; $88000000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10_SRGB $88060000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10 $88720000:; $88740000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10_BT2020_PQ $88750000:; $88820000:; $88840000:; $88850000:; $b0000000:; $b0060000:; $b0720000:; $b0740000:; $b0750000:; $b0820000:; $b0840000:; $b0850000:; $c1060000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A16R16G16B16_FLOAT $c1760000:; else Exit(EINVAL); end; mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin if (data^.submit=0) then begin Result:=dce_handle.RegisterBufferAttribute(data^.attrid,@data^.pixelFormat); end else begin Result:=dce_handle.SubmitBufferAttribute (data^.attrid,@data^.pixelFormat); end; end; mtx_unlock(dce_mtx); Writeln('register_buffer_attr:',data^.attrid,' ', data^.submit,' ', '0x',HexStr(data^.pixelFormat,8),' ', data^.tilingMode,' ', data^.pitchPixel,' ', data^.width,' ', data^.height,' ', '0x',HexStr(data^.options,4)); end; type p_register_buffer_ptrs=^t_register_buffer_ptrs; t_register_buffer_ptrs=packed record canary:QWORD; //arg5 data in dce_flip_control:0:pointer(arg5)^ index :DWORD; //buffer index [0..15] attrid:DWORD; //attribute id [0..3] left :Pointer; //buffer ptr right :Pointer; //Stereo ptr stereo:Integer; end; Function dce_register_buffer_ptrs(dev:p_cdev;data:p_register_buffer_ptrs):Integer; begin Result:=0; if (data^.canary<>$a5a5) then Exit(EINVAL); if (data^.index>15) then Exit(EINVAL); if (data^.attrid>3) then Exit(EINVAL); mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin Result:=dce_handle.RegisterBuffer(@data^.index); end; mtx_unlock(dce_mtx); Writeln('register_buffer_ptrs:',data^.attrid,' ', data^.index,' ', '0x',HexStr(data^.left),' ', '0x',HexStr(data^.right)); end; type p_submit_flip_args=^t_submit_flip_args; t_submit_flip_args=packed record canary :QWORD; //arg5 data in dce_flip_control:0:pointer(arg5)^ bufferIndex:QWORD; flipMode :DWORD; f_0x14 :DWORD; flipArg :QWORD; flipArg2 :QWORD; eop_nz :DWORD; f_0x2c :DWORD; eop_val :PQWORD; f_0x38 :QWORD; rout :PQWORD; //extraout of result end; function TriggerFlipEop(submit_id:QWORD):Integer; begin if (dce_handle=nil) then begin Result:=EINVAL; end else begin Result:=dce_handle.TriggerFlipEop(submit_id); end; end; Function dce_submit_flip(dev:p_cdev;data:p_submit_flip_args):Integer; var submit:t_submit_flip; submit_eop:QWORD; ures:QWORD; begin Result:=0; if (data^.canary<>$a5a5) then Exit(EINVAL); if (Integer(data^.bufferIndex)>15) or (Integer(data^.bufferIndex)<-1) then Exit(EINVAL); if (data^.flipMode>6) then Exit(EINVAL); submit.bufferIndex:=data^.bufferIndex; submit.flipMode :=data^.flipMode; submit.flipArg :=data^.flipArg; submit.flipArg2 :=data^.flipArg2; mtx_lock(dce_mtx); if (dce_handle=nil) then begin Result:=EINVAL; end else begin if (data^.eop_nz=1) then begin // count canary //eop=0x[00000001] [ff] [00] [a5a5] submit_eop:=(QWORD(f_eop_count) shl 32) or QWORD($ff00a5a5); Writeln('submit_eop=0x',HexStr(submit_eop,16)); f_eop_count:=f_eop_count+1; Result:=dce_handle.SubmitFlipEop(@submit,submit_eop); if (Result<>0) then begin Writeln('SubmitFlipEopError=',Result); end; end else begin Result:=dce_handle.SubmitFlip(@submit); if (Result<>0) then begin Writeln('SubmitFlipError=',Result); end; end; end; mtx_unlock(dce_mtx); //print_backtrace_td(stderr); Writeln('submit_flip: ','bufferIndex=',data^.bufferIndex,' ', 'flipMode=',data^.flipMode,' ', 'flipArg=','0x',HexStr(data^.flipArg,16),' ', 'eop_val=','0x',HexStr(data^.eop_val)); if (Result=0) then if (data^.eop_nz=1) then begin Result:=copyout(@submit_eop,data^.eop_val,SizeOf(QWORD)); end; if (data^.rout<>nil) then begin ures:=Result; copyout(@ures,data^.rout,SizeOf(QWORD)); end; end; Function dce_deregister_ident(dev:p_cdev;data:PQWORD):Integer; begin writeln('dce_deregister_ident:',HexStr(data^,16)); kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,data^); Result:=0; end; Function dce_ioctl(dev:p_cdev;cmd:QWORD;data:Pointer;fflag:Integer):Integer; begin Result:=0; //Writeln('dce_ioctl(0x',HexStr(cmd,8),')'); case cmd of $C0308203:Result:=dce_flip_control (dev,data); //SCE_SYS_DCE_IOCTL_FLIP_CONTROL $C0308207:Result:=dce_register_buffer_attr(dev,data); //SCE_SYS_DCE_IOCTL_REGISTER_BUFFER_ATTRIBUTE $C0308206:Result:=dce_register_buffer_ptrs(dev,data); $C0488204:Result:=dce_submit_flip (dev,data); $80088209:Result:=dce_deregister_ident (dev,data); else begin print_error_td('dce_ioctl(0x'+HexStr(cmd,8)+')'); Assert(False); Result:=EINVAL; end; end; end; Function dce_mmap(dev:p_cdev;offset:vm_ooffset_t;paddr:p_vm_paddr_t;nprot:Integer;memattr:p_vm_memattr_t):Integer; var foff:vm_ooffset_t; begin Result:=0; if (offset > $3ffffff) then begin Exit(EINVAL); end; if ((nprot and $ffffffcc)<>0) then begin Exit(EINVAL); end; foff:=offset; //? offset:=((DWORD(offset) mod PAGE_SIZE) + foff); offset:=(foff and $1fffff) { or (uVar1 and $fffffffe00000)}; paddr^:=DWORD(offset) + QWORD(dce_page); end; Function dce_mmap_single(cdev:p_cdev;offset:p_vm_ooffset_t;size:vm_size_t;pobj:p_vm_object_t;nprot:Integer):Integer; var off:vm_ooffset_t; obj:vm_object_t; begin Result:=0; if (size<>PAGE_SIZE) then begin Exit(EINVAL); end; off:=offset^; if ((off and QWORD($fffffffffc003fff))<>0) then //0..3FFC000 begin Exit(EINVAL); end; if (off<>0) then begin Assert(false); end; if (nprot<>$33) then begin Exit(EACCES); end; obj:=vm_pager_allocate(OBJT_DEVICE,cdev,PAGE_SIZE,$33,off); obj^.un_pager.map_base:=dce_page; if (obj=nil) then begin Exit(EINVAL); end; pobj^:=obj; end; Function dce_kqfilter(dev:p_cdev;kn:p_knote):Integer; begin if (kn^.kn_kevent.filter=EVFILT_DISPLAY) then begin knlist_add(@g_video_out_event_flip,kn,1); end; Result:=EINVAL; end; const dce_cdevsw:t_cdevsw=( d_version :D_VERSION; d_flags :0; d_name :'dce'; d_ioctl :@dce_ioctl; d_mmap :@dce_mmap; d_kqfilter :@dce_kqfilter; d_mmap_single :@dce_mmap_single; ); function filt_display_attach(kn:p_knote):Integer; var event_id:WORD; begin kn^.kn_flags:=kn^.kn_flags or EV_CLEAR; { automatically set } if ((kn^.kn_kevent.ident and QWORD($f00000000000))=0) then begin //_display_attach(&kn->kn_kevent); end; kn^.kn_hook:=Pointer(p_proc.p_pid); event_id:=kn^.kn_kevent.ident shr 48; case event_id of EVENTID_FLIP, //SCE_VIDEO_OUT_EVENT_FLIP EVENTID_VBLANK, //SCE_VIDEO_OUT_EVENT_VBLANK EVENTID_SETMODE, //8 EVENTID_POSITION, //12 EVENTID_PREVBLANK, //SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START $0060, $0061, $0062, $0063: begin knlist_add(@g_video_out_event_flip,kn,0); end; else begin //Writeln(stderr,'filt_display_attach:',event_id); //Assert(false,'filt_display_attach'); Result:=EINVAL; end; end; Exit(0); end; procedure filt_display_detach(kn:p_knote); var event_id:WORD; begin event_id:=kn^.kn_kevent.ident shr 48; case event_id of EVENTID_FLIP, //SCE_VIDEO_OUT_EVENT_FLIP EVENTID_VBLANK, //SCE_VIDEO_OUT_EVENT_VBLANK EVENTID_SETMODE, //8 EVENTID_POSITION, //12 EVENTID_PREVBLANK, //SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START $0060, $0061, $0062, $0063: begin knlist_remove(@g_video_out_event_flip,kn,0) end; else; end; if ((kn^.kn_kevent.ident and QWORD($f00000000000))=0) then begin //_display_detach(&kn->kn_kevent); end; end; type t_dce_hint=bitpacked record event_id:bit8; video_id:bit8; flip_arg:bit48; end; t_dce_data=bitpacked record time :bit12; counter :bit4; flip_arg:bit48; end; function filt_display_event(kn:p_knote;hint:QWORD):Integer; var event_id:WORD; video_id:BYTE; counter :BYTE; time :QWORD; begin if (hint=0) then begin Result:=t_dce_data(kn^.kn_kevent.data).counter; end else begin event_id:=kn^.kn_kevent.ident shr 48; video_id:=kn^.kn_kevent.ident shr 40; if (t_dce_hint(hint).event_id=event_id) and (event_id<>$fe) and ((t_dce_hint(hint).video_id xor video_id)=0) then begin time:=rdtsc(); counter:=t_dce_data(kn^.kn_kevent.data).counter; if (counter<>$f) then begin counter:=counter+1; end; kn^.kn_kevent.data:=(time and $fff) or //time (counter shl 12) or //counter (hint and QWORD($ffffffffffff0000)); //flip_arg Result:=1; end else begin Result:=0; end; end; end; const filterops_display:t_filterops=( f_isfd :0; f_attach:@filt_display_attach; f_detach:@filt_display_detach; f_event :@filt_display_event; ); procedure dce_initialize(); begin mtx_init(knlist_lock_flip,'knlist_lock_flip'); mtx_init(dce_mtx,'dce'); knlist_init_mtx(@g_video_out_event_flip,@knlist_lock_flip); mtx_init(vblank.lock,'vblank.lock'); callout_init_mtx(@vblank.callout,vblank.lock,0); kqueue_add_filteropts(EVFILT_DISPLAY,@filterops_display); dce_page:=dev_mem_alloc(1); make_dev(@dce_cdevsw,0,0,0,0666,'dce',[]); end; end.