]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/apm/apm.c
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / apm / apm.c
1 /* apm.c
2  *
3  * Advanced Power Management BIOS 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  * Compiles for intended target environments:
11  *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]
12  *
13  * This library is intended for DOS programs that want to communicate with the APM
14  * BIOS interface on PC systems made since about 1994. Note that on systems built
15  * since 1999 you may want to use the ACPI BIOS instead, however that interface is
16  * quite a bit more complex. */
17  
18 #include <stdio.h>
19 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
20 #include <stdlib.h>
21 #include <stddef.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <malloc.h>
25 #include <ctype.h>
26 #include <fcntl.h>
27 #include <dos.h>
28
29 #include <hw/dos/dos.h>
30 #include <hw/apm/apm.h>
31 #include <hw/8254/8254.h>               /* 8254 timer */
32 #include <hw/8259/8259.h>               /* 8259 PIC */
33
34 struct apm_bios_ctx *apm_bios = NULL;
35
36 #if TARGET_MSDOS == 32
37 static void apm_bios_rm_call(struct dpmi_realmode_call *rc) {
38         __asm {
39                 mov     ax,0x0300
40                 mov     bx,0x0015
41                 xor     cx,cx
42                 mov     edi,rc          ; we trust Watcom has left ES == DS
43                 int     0x31            ; call DPMI
44         }
45 }
46 #endif
47
48 void apm_bios_free() {
49         if (apm_bios) free(apm_bios);
50         apm_bios = NULL;
51 }
52
53 int apm_bios_probe() { /* 8.8 version */
54         apm_bios_free();
55
56         apm_bios = (struct apm_bios_ctx*)malloc(sizeof(*apm_bios));
57         if (apm_bios == NULL) return 0;
58         memset(apm_bios,0,sizeof(*apm_bios));
59
60         apm_bios->version_want = 0x102;
61         apm_bios->major_neg = 1;
62         apm_bios->minor_neg = 0;
63
64 #if TARGET_MSDOS == 32
65         {
66                 struct dpmi_realmode_call rc={0};
67                 rc.eax = 0x5300;        /* AH=0x53 A=0x00 */
68                 apm_bios_rm_call(&rc);
69                 if (!(rc.flags & 1)) { /* CF=0 */
70                         apm_bios->major = (rc.eax >> 8) & 0xFF;
71                         apm_bios->minor = (rc.eax & 0xFF);
72                         apm_bios->signature = (rc.ebx & 0xFFFF);
73                         apm_bios->flags = (rc.ecx & 0xFFFF);
74                 }
75         }
76 #else
77         __asm {
78                 mov     ah,0x53
79                 xor     al,al
80                 xor     bx,bx
81                 int     0x15
82                 jc      err1
83 #if defined(__LARGE__) || defined(__COMPACT__)
84                 push    ds
85                 mov     si,seg apm_bios         ; we need DS = segment of apm_bios variable
86                 mov     ds,si
87                 lds     si,apm_bios             ; DS:SI = apm_bios
88 #else
89                 mov     si,apm_bios
90 #endif
91                 mov     byte ptr [si+0],ah
92                 mov     byte ptr [si+1],al
93                 mov     word ptr [si+2],bx
94                 mov     word ptr [si+4],cx
95 #if defined(__LARGE__) || defined(__COMPACT__)
96                 pop     ds
97 #endif
98 err1:
99         }
100 #endif
101
102         return (apm_bios->signature == APM_PM_SIG);
103 }
104
105 void apm_bios_update_capabilities() {
106         apm_bios->capabilities = 0;
107         apm_bios->batteries = 0;
108
109 #if TARGET_MSDOS == 32
110         {
111                 struct dpmi_realmode_call rc={0};
112                 rc.eax = 0x5310;
113                 apm_bios_rm_call(&rc);
114                 if (!(rc.flags & 1)) { /* CF=0 */
115                         apm_bios->capabilities = rc.ecx & 0xFFFF;
116                         apm_bios->batteries = rc.ebx & 0xFF;
117                 }
118         }
119 #else
120         {
121                 unsigned char bat=0,err=0;
122                 unsigned short cap=0;
123
124                 __asm {
125                         mov     ax,0x5310
126                         xor     bx,bx
127                         int     0x15
128                         jnc     err1
129                         mov     err,ah
130 err1:                   mov     bat,bl
131                         mov     cap,cx
132                 }
133
134                 if (err == 0) {
135                         apm_bios->capabilities = cap;
136                         apm_bios->batteries = bat;
137                 }
138         }
139 #endif
140 }
141
142 /* FIXME: For the protected mode calls, this code needs to save AX, BX, CX, (E)SI, and (E)DI
143  *        which contain all the info needed for protected mode selectors */
144 int apm_bios_connect(int n) {
145         unsigned char err=0;
146
147         if (apm_bios == NULL)
148                 return 0;
149         if (n < APM_CONNECT_NONE || n > APM_CONNECT_32_PM)
150                 return 0;
151         if (n == APM_CONNECT_NONE)
152                 n = 0x04; /* AH=0x04 disconnect function */
153
154         /* NTS: for now, only the real-mode interface */
155         if (n == APM_CONNECT_16_PM || n == APM_CONNECT_32_PM)
156                 return 0; /* anything but real mode not supported */
157
158 #if TARGET_MSDOS == 32
159         {
160                 struct dpmi_realmode_call rc={0};
161                 rc.eax = 0x5300+n;      /* AH=0x53 A=0x01-0x04 */
162                 apm_bios_rm_call(&rc);
163                 if (rc.flags & 1) { /* CF=1 */
164                         err = (rc.eax >> 8) & 0xFF;
165                 }
166         }
167 #else
168         __asm {
169                 mov     ah,0x53
170                 mov     al,byte ptr n
171                 xor     bx,bx
172                 int     0x15
173                 jnc     err1
174                 mov     err,ah
175 err1:
176         }
177 #endif
178
179         /* translate "n" back to enum */
180         if (n == 0x04) n = APM_CONNECT_NONE;
181
182         /* success: BIOS returns success, or disconnect (AH=4) command says "not connected" */
183         if (err == 0x00 || (err == 0x03 && n == 0x04)) {
184                 apm_bios->connection = n;
185         }
186         else if (err == 0x02) { /* err code 2 real mode interface connected */
187                 apm_bios->connection = APM_CONNECT_REALMODE;
188         }
189         else if (err == 0x05) { /* err code 5 16-bit prot mode interface connected */
190                 apm_bios->connection = APM_CONNECT_16_PM;
191         }
192         else if (err == 0x07) { /* err code 2 32-bit prot mode interface connected */
193                 apm_bios->connection = APM_CONNECT_32_PM;
194         }
195
196         /* if transitioning to an interface, negotiate a newer API */
197         if (err == 0x00 && n > APM_CONNECT_NONE) {
198                 unsigned int neg_ver = apm_bios->version_want;
199                 unsigned short ww = 0;
200
201                 apm_bios->major_neg = 1;
202                 apm_bios->minor_neg = 0;
203                 if (apm_bios->major == 1 && apm_bios->minor != 0 && neg_ver >= 0x101 && neg_ver <= 0x1FF) {
204 #if TARGET_MSDOS == 32
205                         {
206                                 struct dpmi_realmode_call rc={0};
207                                 rc.eax = 0x530E;
208                                 rc.ecx = neg_ver;
209                                 apm_bios_rm_call(&rc);
210                                 if (!(rc.flags & 1)) { /* CF=0 */
211                                         ww = rc.eax & 0xFFFF;
212                                 }
213                         }
214 #else
215                         __asm {
216                                 mov     ax,0x530E
217                                 xor     bx,bx
218                                 mov     cx,neg_ver
219                                 int     0x15
220                                 jc      err2
221                                 mov     ww,ax
222 err2:
223                         }
224 #endif
225
226                         if (ww >= 0x101 && ww <= 0x1FF) {
227                                 apm_bios->major_neg = ww >> 8;
228                                 apm_bios->minor_neg = ww & 0xFF;
229                         }
230                 }
231         }
232
233         if (n > APM_CONNECT_NONE && apm_bios->connection == APM_CONNECT_NONE)
234                 apm_bios_update_capabilities();
235
236         apm_bios->last_error = err;
237         return (apm_bios->connection == n);
238 }
239
240 int apm_bios_cpu_busy() {
241         unsigned char err=0;
242
243 #if TARGET_MSDOS == 32
244 {
245         struct dpmi_realmode_call rc={0};
246         rc.eax = 0x5306;
247         apm_bios_rm_call(&rc);
248         if (rc.flags & 1) { /* CF=1 */
249                 err = (rc.eax >> 8) & 0xFF;
250         }
251 }
252 #else
253 __asm {
254         mov     ax,0x5306
255         int     0x15
256         jnc     err1
257         mov     err,ah
258 err1:
259 }
260 #endif
261
262         return (err == 0);
263 }
264
265 int apm_bios_cpu_idle() {
266         unsigned char err=0;
267
268 #if TARGET_MSDOS == 32
269         {
270                 struct dpmi_realmode_call rc={0};
271                 rc.eax = 0x5305;
272                 apm_bios_rm_call(&rc);
273                 if (rc.flags & 1) { /* CF=1 */
274                         err = (rc.eax >> 8) & 0xFF;
275                 }
276         }
277 #else
278         __asm {
279                 mov     ax,0x5305
280                 int     0x15
281                 jnc     err1
282                 mov     err,ah
283 err1:
284         }
285 #endif
286
287         return (err == 0);
288 }
289
290 signed long apm_bios_pm_evnet() {
291         unsigned short ev=0,info=0;
292
293 #if TARGET_MSDOS == 32
294         {
295                 struct dpmi_realmode_call rc={0};
296                 rc.eax = 0x530B;
297                 apm_bios_rm_call(&rc);
298                 if (!(rc.flags & 1)) { /* CF=0 */
299                         ev = rc.ebx & 0xFFFF;
300                         info = rc.ecx & 0xFFFF;
301                 }
302         }
303 #else
304         __asm {
305                 mov     ax,0x530B
306                 int     0x15
307                 jc      err1
308                 mov     ev,bx
309                 mov     info,cx
310 err1:
311         }
312 #endif
313
314         return (ev == 0 ? -1LL : (unsigned long)ev);
315 }
316
317 void apm_bios_update_status() {
318 #if TARGET_MSDOS == 32
319         {
320                 struct dpmi_realmode_call rc={0};
321                 rc.eax = 0x530A;
322                 apm_bios_rm_call(&rc);
323                 if (!(rc.flags & 1)) { /* CF=0 */
324                         apm_bios->status_ac = (rc.ebx >> 8) & 0xFF;
325                         apm_bios->status_battery = (rc.ebx & 0xFF);
326                         apm_bios->status_battery_flag = (rc.ecx >> 8) & 0xFF;
327                         apm_bios->status_battery_life = (rc.ecx & 0xFF);
328                         apm_bios->status_battery_time = rc.edx & 0xFFFF;
329                 }
330         }
331 #else
332         {
333                 unsigned short b=0,c=0,d=0;
334
335                 __asm {
336                         mov     ax,0x530A
337                         int     0x15
338                         jc      err1
339                         mov     b,bx
340                         mov     c,cx
341                         mov     d,dx
342 err1:
343                 }
344
345                 apm_bios->status_ac = (b >> 8) & 0xFF;
346                 apm_bios->status_battery = (b & 0xFF);
347                 apm_bios->status_battery_flag = (c >> 8) & 0xFF;
348                 apm_bios->status_battery_life = (c & 0xFF);
349                 apm_bios->status_battery_time = d & 0xFFFF;
350         }
351 #endif
352 }
353
354 int apm_bios_power_state(unsigned short state) {
355         unsigned char err=0;
356
357 #if TARGET_MSDOS == 32
358         {
359                 struct dpmi_realmode_call rc={0};
360                 rc.eax = 0x5307;
361                 rc.ebx = 0x0001;
362                 rc.ecx = state;
363                 apm_bios_rm_call(&rc);
364                 if (rc.flags & 1) { /* CF=0 */
365                         err = (rc.eax >> 8) & 0xFF;
366                 }
367         }
368 #else
369         {
370                 __asm {
371                         mov     ax,0x5307
372                         mov     bx,0x0001
373                         mov     cx,state
374                         int     0x15
375                         jnc     err1
376                         mov     err,ah
377 err1:
378                 }
379         }
380 #endif
381
382         return (err == 0);
383 }
384