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