]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/cpu.h
f84974bbef8319a3effcffb1d5dbe6410f68b171
[16.git] / src / lib / doslib / cpu.h
1 /* cpu.h
2  *
3  * Runtime CPU detection library.
4  * (C) 2009-2012 Jonathan Campbell.
5  * Hackipedia DOS library.
6  *
7  * This code is licensed under the LGPL.
8  * <insert LGPL legal text here>
9  *
10  */
11
12 #ifndef __HW_CPU_CPU_H
13 #define __HW_CPU_CPU_H
14
15 #include <stdint.h>
16 #include <dos.h>
17
18 #if !defined(FAR)
19 # if defined(TARGET_WINDOWS)
20 #  include <windows.h>
21 # else
22 #  if TARGET_MSDOS == 32
23 #   define FAR
24 #  else
25 #   define FAR far
26 #  endif
27 # endif
28 #endif
29
30 /* FIX: Open Watcom does not provide inpd/outpd in 16-bit real mode, so we have to provide it ourself */
31 /*      We assume for the same stupid reasons the pragma aux function can't be used because it's a 386 level instruction */
32 #if TARGET_MSDOS == 16
33 uint32_t __cdecl inpd(uint16_t port);
34 void __cdecl outpd(uint16_t port,uint32_t data);
35 #endif
36
37 #if TARGET_MSDOS == 16
38 static inline uint32_t ptr2phys(void far *p) {
39         return (((uint32_t)FP_SEG(p)) << 4UL) +
40                 ((uint32_t)FP_OFF(p));
41 }
42 #endif
43
44 #pragma pack(push,1)
45 struct cpu_cpuid_features {
46         union {
47                 uint32_t                raw[4]; /* EAX, EBX, EDX, ECX */
48                 struct {
49                         uint32_t        todo[4];
50                 } f;
51         } a;
52 };
53
54 struct cpu_cpuid_ext_features {
55         union {
56                 uint32_t                raw[4]; /* EAX, EBX, ECX, EDX */
57                 struct {
58                         uint32_t        todo[4];
59                 } f;
60         } a;
61 };
62
63 struct cpu_cpuid_ext_cache_tlb {
64         union {
65                 uint32_t                raw[4]; /* EAX, EBX, ECX, EDX */
66                 struct {
67                         uint32_t        todo[4];
68                 } f;
69         } a;
70 };
71
72 struct cpu_cpuid_ext_cache_tlb_l2 {
73         union {
74                 uint32_t                raw[4]; /* EAX, EBX, ECX, EDX */
75                 struct {
76                         uint32_t        todo[4];
77                 } f;
78         } a;
79 };
80
81 struct cpu_cpuid_ext_longmode {
82         union {
83                 uint32_t                raw[4]; /* EAX, EBX, ECX, EDX */
84                 struct {
85                         uint32_t        todo[4];
86                 } f;
87         } a;
88 };
89
90 struct cpu_cpuid_ext_apm {
91         union {
92                 uint32_t                raw[1]; /* EAX */
93                 struct {
94                         uint32_t        todo[1];
95                 } f;
96         } a;
97 };
98
99 struct cpuid_result {
100         uint32_t                        eax,ebx,ecx,edx;
101 };
102 #pragma pack(pop)
103
104 /* "Basic" CPU level */
105 enum {
106         CPU_8086=0,             /* 0 */
107         CPU_186,
108         CPU_286,
109         CPU_386,
110         CPU_486,
111         CPU_586,
112         CPU_686,
113         CPU_MAX
114 };
115
116 extern const char *                     cpu_basic_level_str[CPU_MAX];
117 extern char                             cpu_cpuid_vendor[13];
118 extern struct cpu_cpuid_features        cpu_cpuid_features;
119 extern signed char                      cpu_basic_level;
120 extern uint32_t                         cpu_cpuid_max;
121 extern unsigned char                    cpu_flags;
122 extern uint16_t                         cpu_tmp1;
123
124 /* compatability */
125 #define cpu_v86_active (cpu_flags & CPU_FLAG_V86_ACTIVE)
126
127 #define cpu_basic_level_to_string(x) (x >= 0 ? cpu_basic_level_str[x] : "?")
128
129 /* CPU flag: CPU supports CPUID */
130 #define CPU_FLAG_CPUID                  (1 << 0)
131 #define CPU_FLAG_FPU                    (1 << 1)
132 #define CPU_FLAG_CPUID_EXT              (1 << 2)
133 #define CPU_FLAG_V86_ACTIVE             (1 << 3)
134 #define CPU_FLAG_PROTECTED_MODE         (1 << 4)
135 #define CPU_FLAG_PROTECTED_MODE_32      (1 << 5)
136 /*      ^ Windows-specific: we are not only a 16-bit Win16 app, but Windows is running in 386 enhanced mode
137  *                          and we can safely use 32-bit registers and hacks. This will always be set for
138  *                          Win32 and 32-bit DOS, obviously. If set, PROTECTED_MODE is also set. */
139 #define CPU_FLAG_DONT_WRITE_RDTSC       (1 << 6)
140
141 void cpu_probe();
142 int cpu_basic_probe(); /* external assembly language function */
143
144 static void _cli();
145 #pragma aux _cli = "cli"
146 static void _sti();
147 #pragma aux _sti = "sti"
148
149 static inline void _sti_if_flags(unsigned int f) {
150         if (f&0x200) _sti(); /* if IF bit was set, then restore interrupts by STI */
151 }
152
153 /* NTS: remember for Watcom: 16-bit realmode sizeof(int) == 2, 32-bit flat mode sizeof(int) == 4 */
154 static unsigned int get_cpu_flags();
155 #if TARGET_MSDOS == 32
156 #pragma aux get_cpu_flags = \
157         "pushfd"        \
158         "pop    eax"    \
159         value [eax];
160 #else
161 #pragma aux get_cpu_flags = \
162         "pushf"         \
163         "pop    ax"     \
164         value [ax];
165 #endif
166
167 static void set_cpu_flags(unsigned int f);
168 #if TARGET_MSDOS == 32
169 #pragma aux set_cpu_flags = \
170         "push   eax"    \
171         "popfd  "       \
172         parm [eax];
173 #else
174 #pragma aux set_cpu_flags = \
175         "push   ax"     \
176         "popf   "       \
177         parm [ax];
178 #endif
179
180 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
181 /* Watcom does not offer int86/int386 for Win32s/Win9x/NT/etc */
182 #else
183 static inline void just_int86(unsigned char c,union REGS *r1,union REGS *r2) {
184 # ifdef __386__
185         int386(c,r1,r2);
186 # else
187         int86(c,r1,r2);
188 # endif
189 }
190 #endif
191
192 #if TARGET_MSDOS == 32
193 static inline void cpu_cpuid(uint32_t idx,struct cpuid_result *x);
194 #pragma aux cpu_cpuid = \
195         ".586p" \
196         "xor ebx,ebx" \
197         "mov ecx,ebx" \
198         "mov edx,ebx" \
199         "cpuid" \
200         "mov [esi],eax" \
201         "mov [esi+4],ebx" \
202         "mov [esi+8],ecx" \
203         "mov [esi+12],edx" \
204         parm [eax] [esi] \
205         modify [ebx ecx edx]
206 #else
207 void cpu_cpuid(uint32_t idx,struct cpuid_result *x);
208 #endif
209
210 #if TARGET_MSDOS == 32
211 static inline uint64_t cpu_rdmsr(const uint32_t idx);
212 #pragma aux cpu_rdmsr = \
213         ".586p" \
214         "rdmsr" \
215         parm [ecx] \
216         value [edx eax]
217
218 static inline void cpu_wrmsr(const uint32_t idx,const uint64_t val);
219 #pragma aux cpu_wrmsr = \
220         ".586p" \
221         "wrmsr" \
222         parm [ecx] [edx eax]
223 #else
224 /* This is too much code to inline insert everywhere---unless you want extra-large EXEs.
225  * It's better to conform to Watcom's register calling convention and make it a function.
226  * Note that if you look at the assembly language most of the code is shuffling the values
227  * around to convert EDX:EAX to AX:BX:CX:DX and disabling interrupts during the call. */
228 /* see CPUASM.ASM */
229 uint64_t cpu_rdmsr(const uint32_t idx);
230 void cpu_wrmsr(const uint32_t idx,const uint64_t val);
231 #endif
232
233 #endif /* __HW_CPU_CPU_H */