cpufreq.as [CPU周波数モジュール]

;*******************************************************************************
; CPU Frequency module  [2007.02.23]
;   CPUID module, CPUFREQ module
;  Programmed in HSP Ver3.1 ONION software (http://hsp.tv/)
;   Copyright (C) 2007 by abo, all rights reserved.
;*******************************************************************************

#ifdef __hsp30__
#ifndef __CPUFREQ__
#define global __CPUFREQ__

#module "cpufreq"
/************************************************************************
    CPUID p1,p2 ・・・CPU情報を取得
    p1:情報を受け取る変数
    p2:EAXの初期値(省略時=0)
    ---------------------------
    ・p2をEAXにセットしてcpuidを実行し、EAX,EBX,ECX,EDXの値をp1.0〜3にセット
     します
    ・p2を0にして実行すると、まずcpuidが使用できるか(EFLAGSのbit21を変更できるか)
     をチェックします
     このときp1に0がセットされると、CPUIDは使用できません

    RDTSC p1    ・・・TSC(タイムスタンプ・カウンタ)を取得
    p1:情報を受け取る変数(符号なし64bit整数)
    ---------------------------
    ・p1は符号なし32bit整数、TSCは符号なし64bit整数として扱われます
    ・TSCは64bitの大きさを持ち、CPUがリセット時に0から始まり、クロック・
     サイクルごとに+1されるカウンタです
    ・この命令が使用できるかをCPUIDであらかじめ調べておく必要があります
     調べ方:
        flag=0
        CPUID reg,0
        if reg{
            CPUID reg,1
            flag=reg.3>>4&1
        }
        if flag==1:mes "使用可能" :else :mes "使用不可能"

**********************************************************************/
#deffunc __init_cpuid
#define global CPUID(%1,%2=0) prm@cpufreq=varptr(%1),%2 :\
    res=callfunc( prm@cpufreq, varptr(fnCpuid@cpufreq), 2 )
#define global RDTSC(%1) prm@cpufreq=varptr(%1) :\
    res=callfunc( prm@cpufreq, varptr(fnRDTSC@cpufreq), 1)

;   ****************************************************************************
;   asmlist
    xdim fnCpuid,16
    asm fnCpuid,{"
        ORG     0
        PUSH    EBP
        MOV     EBP,ESP
        PUSH    EBX
        PUSH    ESI
        MOV     EAX,[EBP+0x0C]  ;EAX <- p2
        OR      EAX,EAX
        JNZ     L0              ;if p2!0
        PUSHFD
        POP     EAX             ;EFLAGS Register
        MOV     EBX,EAX         ;EBX <- pre EFLAGS
        XOR     EAX,0x00200000  ;Reverse ID Flag(bit21)
        PUSH    EAX
        POPFD
        PUSHFD
        POP     ECX             ;ECX <- new EFLAGS
        XOR     EAX,EAX         ;EAX <- 0[Initial setting]
        CMP     ECX,EBX
        JZ      L1              ; if pre = new : not CPUID
    L0:
        CPUID
        MOV     ESI,[EBP+08]    ; varptr(p1)
        MOV     [ESI],EAX       ; p1(0) <- EAX
        MOV     [ESI+04],EBX    ; p1(1) <- EBX
        MOV     [ESI+08],ECX    ; p1(2) <- ECX
        MOV     [ESI+0x0C],EDX  ; p1(3) <- EDX
    L1:
        POP     ESI
        POP     EBX
        LEAVE
        RET
        END
    "}

;   ****************************************************************************
    xdim        fnRdtsc,4
    asm fnRdtsc,{"
        ORG     0
        MOV     ECX,[ESP+04]    ; ECX <- varptr(p1)
        RDTSC                   ; EDX,EAX <- TSC
        MOV     [ECX],EAX       ; p1(0) <- EAX
        MOV     [ECX+04],EDX    ; p1(1) <- EDX
        RET
        END
    "}

    dim prm,2 : dim reg,4 : fCpuid@=0 : fRdtsc@=0
    CPUID reg
    if reg {
        fCpuid@=1
        CPUID reg,1
        fRdtsc@=reg.3>>4&1
    }
    return



// getms.dll  HSP3移植版
#define global FREQ_TERM    1000

#deffunc __init_cpufreq
    dim PreQPC,2 : dim StrQPC,2 : dim EndQPC,2 : dim QPF,2
    dim PreTSC,2 : dim StrTSC,2 : dim EndTSC,2
    dim NowQPC,2 : dim OldQPC,2
    dim ER0,2 : dim ER1,2 : dim ER2,2 : dim ER3,2

    dim TIMECAPS,2                      ;TIMECAPS構造体
    timeGetDevCaps varptr(TIMECAPS),8   ;&tc , sizeof(TIMECAPS)

    return

; -----------------------------------------------------------------------------
; aSleep - Sleep関数1msのwait (CPU使用率13%)
; -----------------------------------------------------------------------------
#deffunc aSleep int time

    timeBeginPeriod 1                   ;tc.wPeriodMin

    repeat time
        await
        Sleep 1
    loop

    timeEndPeriod 1

    return

; -----------------------------------------------------------------------------
; qpctime - 高分解能パフォーマンスカウンタで時間を計る
;
;   戻り値:前回呼び出されてからの経過時間[sec]
; -----------------------------------------------------------------------------
#defcfunc Qpctime
;   timeGetDevCaps varptr(TIMECAPS),8   ;&tc , sizeof(TIMECAPS)
    timeBeginPeriod 1                   ;tc.wPeriodMin

    QueryPerformanceCounter varptr(NowQPC)
    QueryPerformanceFrequency varptr(QPF)

    ER0=LLSUB(NowQPC,OldQPC)
    OldQPC=LLCOPY(NowQPC)

    timeEndPeriod 1

    return LLTOD(ER0)/LLTOD(QPF)

; -----------------------------------------------------------------------------
; Getfreq - CPUのクロック周波数を得る
;
;   Getfreq(p1, p2)
;       p1: 測定モード
;       -----------------------------------------------------------
;           0   高分解能パフォーマンスカウンタ[QueryPerformanceCounter]
;           1   マルチメディアタイマ[MultiMediaTimer]
;           2   ms 単位でスレッドの実行を中断する[Sleep]
;       -----------------------------------------------------------
;       p2: 測定時間[ms]
; -----------------------------------------------------------------------------
#define global ctype Getfreq(%1,%2=FREQ_TERM) _Getfreq(%1,%2)
#defcfunc _Getfreq int flg,int p3
    if (flg<0 || flg>2) : return -1         ;error

    timeBeginPeriod 1                   ;tc.wPeriodMin

    aSleep 100

    if (flg==0) {   ;----- Sleep -----
        RDTSC PreTSC
        RDTSC StrTSC
        Sleep p3;   // ミリ秒
        RDTSC EndTSC
        ER0=LLSUB(EndTSC,StrTSC)
        ER1=LLSUB(StrTSC,PreTSC)
        cpuclock=(LLTOD(ER0)-LLTOD(ER1))/p3/1000
    }
    if (flg==1) {   ;----- MultiMediaTimer -----
        StrTime=_timeGetTime() : TrgTime=StrTime
        repeat
            StrTime=_timeGetTime() : if StrTime>TrgTime : break;    // ミリ秒
        loop
        TrgTime=StrTime
        repeat
            StrTime=_timeGetTime() : if StrTime>TrgTime : break;    // ミリ秒
        loop
        TrgTime=StrTime+p3

        RDTSC PreTSC
        RDTSC StrTSC
        repeat
            StrTime=_timeGetTime() : if StrTime>=TrgTime : break
        loop;   // ミリ秒
        RDTSC EndTSC
        EndTime=StrTime : StrTime=TrgTime-p3 
        ER0=LLSUB(EndTSC,StrTSC)
        ER1=LLSUB(StrTSC,PreTSC)

        cpuclock=(LLTOD(ER0)-LLTOD(ER1))/(Endtime-StrTime)/1000
    }
    if (flg==2) {   ;----- QueryPerformanceCounter -----
        StrTime=_timeGetTime() : TrgTime=StrTime
        repeat
            StrTime=_timeGetTime() : if StrTime>TrgTime : break;    // ミリ秒
        loop
        TrgTime=StrTime
        repeat
            StrTime=_timeGetTime() : if StrTime>TrgTime : break;    // ミリ秒
        loop
        TrgTime=StrTime+p3

        QueryPerformanceCounter varptr(PreQPC)
        QueryPerformanceCounter varptr(StrQPC)
        RDTSC StrTSC
        repeat
            StrTime=_timeGetTime() : if StrTime>=TrgTime : break
        loop;   // ミリ秒
        QueryPerformanceCounter varptr(EndQPC)
        RDTSC EndTSC
        QueryPerformanceFrequency varptr(QPF)

        ER0=LLSUB(EndTSC,StrTSC)
        ER1=LLSUB(EndQPC,StrQPC)
        ER2=LLSUB(StrQPC,PreQPC)

        cpuclock=LLTOD(ER0)*LLTOD(QPF)/(LLTOD(ER1)-LLTOD(ER2))/1000000  
    }

    timeEndPeriod 1

    return cpuclock

#global

__init_cpuid
__init_cpufreq

#endif  ; __CPUFREQ__
#endif  ; __hsp30__