반응형

Intel® 64 and IA-32 Architectures Software Developer’s Manual을 확인해 보자.

 

 

RDTSC—Read Time-Stamp Counter

Reads the current value of the processor’s time-stamp counter (a 64-bit MSR) into the EDX:EAX registers. The EDX register is loaded with the high-order 32 bits of the MSR and the EAX register is loaded with the low-order 32 bits. (On processors that support the Intel 64 architecture, the high-order 32 bits of each of RAX and RDX are cleared.) The processor monotonically increments the time-stamp counter MSR every clock cycle and resets it to 0 whenever the processor is reset.

RDTSC 인스트럭션은 프로세서의 time-stamp counter를 EDX:EAX 레지스터로 가져온다. EDX에는 상위 32비트가 저장되고 EAX에는 하위 32비트가 저장된다.

 

EDX, EAX에 저장되는 값을 확인해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
 
    PRINT_STRING 'High-order 32 bits of Time Stamp Counter: '
    PRINT_HEX 4, edx
    NEWLINE
    NEWLINE
    
    PRINT_STRING 'Low-order 32 bits of Time Stamp Counter: '
    PRINT_HEX 4, eax
    NEWLINE
    NEWLINE
    
    PRINT_STRING 'Time Stamp Counter: '    
    shl rdx, 32
    xor rdx, rax
    PRINT_HEX 8, rdx
 
    xor rax, rax
   ret
 

 

 

EDX, EAX의 값을 확인한다.

 

EAX 레지스터에 저장된 값을 이용해 1~10까지의 랜덤 넘버를 추출해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
    
    ;eax에 저장되는 값을 사용한다.
    PRINT_STRING 'Low-order 32 bits of Time Stamp Counter (dividend): '
    PRINT_HEX 4, eax
    NEWLINE
    NEWLINE
    
    ;2byte 이상 나누기 연산시 div 는 dx, ax를 사용한다.
    ;나누어지는 값을 dx:ax에 나누어 삽입해야 하므로 edx를 0으로 만들고 eax의
    ;값 중 2바이트만 사용한다.
    xor edx, edx
    and eax, 0x0000ffff
    PRINT_STRING 'DX: '
    PRINT_HEX 2, dx
    NEWLINE
    PRINT_STRING 'AX: '
    PRINT_HEX 2, ax
    NEWLINE
    NEWLINE
    
    mov bx, 0xa ;나누는 수를 10으로 설정한다.
    PRINT_STRING 'Divisor: '
    PRINT_HEX 2, bx
    NEWLINE
        
    div bx ;bx(10)로 나눈다. 나머지는 0~9가 나온다.
    PRINT_STRING 'Quotient: '
    PRINT_HEX 2, ax ;몫은 ax에 저장된다.
    NEWLINE
    PRINT_STRING 'Remainder: '
    inc dx
    ;나머지는 dx에 저장된다. 1을 더해서 범위를 1~10으로 맞춘다.
    PRINT_HEX 2, dx
    
    xor rax, rax
   ret
 

 

 

 

Remainder로 1~10의 숫자가 무작위로 추출된다.

 

그런데 실제로 여러번 해 보면 홀수만 나오는 것을 알 수 있다. 내 컴퓨터에서만 그러는건지는 모르겠지만 rdtsc 명령어가 (EAX 레지스터에)짝수만 반환하기 때문이다. (마지막에 1을 더하므로 결국 홀수가 된다)

 

EDX 레지스터의 값을 사용해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
%include "io64.inc"
 
section .text
global CMAIN
CMAIN:
    rdtsc
    
    ;edx에 저장되는 값을 사용한다.
    PRINT_STRING 'High-order 32 bits of Time Stamp Counter (dividend): '
    PRINT_HEX 4, edx
    NEWLINE
    NEWLINE
    
    ;2byte 이상 나누기 연산시 div 는 dx, ax를 사용한다.
    ;나누어지는 값을 dx:ax에 나누어 삽입해야 하므로 edx의 값을 eax에 저장하고
    ;edx를 0으로 만든다. 그리고 eax의 값 중 2바이트만 사용한다.
    mov eax, edx    
    xor edx, edx
    and eax, 0x0000ffff
    PRINT_STRING 'DX: '
    PRINT_HEX 2, dx
    NEWLINE
    PRINT_STRING 'AX: '
    PRINT_HEX 2, ax
    NEWLINE
    NEWLINE
    
    mov bx, 0xa ;
    PRINT_STRING 'Divisor: '
    PRINT_HEX 2, bx
    NEWLINE
        
    div bx
    PRINT_STRING 'Quotient: '
    PRINT_HEX 2, ax
    NEWLINE
    PRINT_STRING 'Remainder: '
    inc dx
    PRINT_HEX 2, dx
 
    xor rax, rax
   ret
 

 

 

홀수, 짝수 상관없이 1~10의 숫자가 무작위로 추출된다.

하지만 EDX 레지스터의 값은 일반적인 시간의 '초' 단위와 비슷한 간격으로 바뀐다. 프로그램을 빠르게 반복 실행하면 동일한 숫자가 반복되어 나오는걸 알 수 있다. 빠른 반복 추출이 필요하다면 EAX 레지스터의 값 중 하위 2바이트가 아닌 중간 2바이트 숫자를 이용하는 등 다른 방법으로 랜덤 넘버를 추출한다.

 

※ 주의

DIV 연산시 나누는 수가 작아 몫이 너무 크게 되면 ax가 저장할 수 있는 용량(2바이트)을 넘어서게 되고 Program received signal SIGFPE, arithmetic exception 메세지가 나타난다. Floating-Point Exception을 의미하지만 0으로 나누는 연산과 같은 모든 산술 연산 에러를 포함한다.

Program Error Signals

 

SIGFPE 예외 발생시 프로그램이 멈추게 된다. 디버깅해야 메세지를 확인 할 수 있다.

 

반응형
Posted by J-sean
:
반응형

숫자(1~99,999)와 디스플레이 크기(1~10) s를 입력하면 [(2s+3)행 X (s+2)열] 크기의 디지털 숫자로 표시하는 프로그램을 만들어 보자. 각 숫자 사이에는 1줄의 공백이 있다.


숫자 5,238을 s=1의 크기로 표시한 예.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
 
using namespace std;
 
int main()
{
    const int MAX_DIGIT = 5;
    const int MAX_SCALE = 10;
 
    char hor_bar[2][MAX_SCALE + 1= { {"          "}, {"----------"} };
    char ver_bar[2][2= { {" "}, {"|"} };
 
    char hor_dig[10][3= { {101}, {000}, {111}, {111}, {010},
        {111}, {111}, {100}, {111}, {111} };    // 0~9
    char ver_dig[10][4= { {1111}, {0101}, {0110}, {0101}, {1101},
        {1001}, {1011}, {0101}, {1111}, {1101} };    // 0~9
    //  -
    // | |
    //  -
    // | |
    //  -
    // 1 digit = 가로선 3개(3줄), 세로선 4개(2줄)
    //             최소 5행 3열
 
    char digits[5][(MAX_SCALE + 3* MAX_DIGIT] = { '\0', };    // 각 숫자 사이에 빈칸 + 마지막 개행 문자 = MAX_SCALE + 3.
                                                                // null문자 '\0'으로 초기화.
    char number[MAX_DIGIT + 1];
    cout << "Enter number(1~99,999): ";
    cin >> number;
 
    int scale;
    cout << "Enter scale(1~10): ";
    cin >> scale;
 
    // scale에 맞게 가로선 길이 조정.
    hor_bar[0][scale] = '\0';
    hor_bar[1][scale] = '\0';
 
    int count_number = strlen(number);
 
    for (int i = 0; i < count_number; i++)
    {
        int real_number = number[i] - '0';    // 문자를 숫자로 변환.
 
        // 두 번째 숫자부터 각 숫자마다 빈 칸 삽입.
        if (i > 0)
            for (int j = 0; j < 5; j++)
                strcat_s(digits[j], sizeof(digits[j]), " ");
 
        // 첫 번째 줄 삽입.
        strcat_s(digits[0], sizeof(digits[0]), " ");
        strcat_s(digits[0], sizeof(digits[0]), hor_bar[hor_dig[real_number][0]]);
        strcat_s(digits[0], sizeof(digits[0]), " ");
 
        // 두 번째 줄 삽입.
        strcat_s(digits[1], sizeof(digits[1]), ver_bar[ver_dig[real_number][0]]);
        strcat_s(digits[1], sizeof(digits[1]), hor_bar[0]);
        strcat_s(digits[1], sizeof(digits[1]), ver_bar[ver_dig[real_number][1]]);
 
        // 세 번째 줄 삽입.
        strcat_s(digits[2], sizeof(digits[2]), " ");
        strcat_s(digits[2], sizeof(digits[2]), hor_bar[hor_dig[real_number][1]]);
        strcat_s(digits[2], sizeof(digits[2]), " ");
 
        // 네 번째 줄 삽입.
        strcat_s(digits[3], sizeof(digits[3]), ver_bar[ver_dig[real_number][2]]);
        strcat_s(digits[3], sizeof(digits[3]), hor_bar[0]);
        strcat_s(digits[3], sizeof(digits[3]), ver_bar[ver_dig[real_number][3]]);
 
        // 다섯 번째 줄 삽입.
        strcat_s(digits[4], sizeof(digits[4]), " ");
        strcat_s(digits[4], sizeof(digits[4]), hor_bar[hor_dig[real_number][2]]);
        strcat_s(digits[4], sizeof(digits[4]), " ");
    }
 
    // 첫 번째 줄 출력
    cout << digits[0<< endl;
 
    // 두 번째 줄 출력
    for (int i = 0; i < scale; i++)
        cout << digits[1<< endl;
 
    // 세 번째 줄 출력
    cout << digits[2<< endl;
 
    // 네 번째 줄 출력
    for (int i = 0; i < scale; i++)
        cout << digits[3<< endl;
 
    // 다섯 번째 줄 출력
    cout << digits[4<< endl;
 
    return 0;
}


잘못된 입력에 대한 처리는 생략되어 있다.


실행 결과.



반응형
Posted by J-sean
: