unset ld env before running command
[ginge.git] / loader / emu.c
1 /*
2  * GINGE - GINGE Is Not Gp2x Emulator
3  * (C) notaz, 2010-2011,2016
4  *
5  * This work is licensed under the MAME license, see COPYING file for details.
6  */
7 // a "gentle" reminder
8 #ifdef __ARM_EABI__
9 #error loader is meant to be OABI!
10 #endif
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <alloca.h>
15 #include <ctype.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <sys/mman.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <signal.h>
23 #include <asm/ucontext.h>
24 #include <errno.h>
25 #include <time.h>
26 #include <sched.h>
27 #include <sys/resource.h>
28 #include <sys/ioctl.h>
29 #include <sys/syscall.h>
30 #include <linux/soundcard.h>
31 #include <linux/fb.h>
32 #include <linux/futex.h>
33
34 #include "header.h"
35 #include "../common/host_fb.h"
36 #include "../common/cmn.h"
37 #include "syscalls.h"
38 #include "realfuncs.h"
39 #include "llibc.h"
40
41 #if (DBG & 2) && !(DBG & 4)
42 #define LOG_IO_UNK
43 #endif
44 #if (DBG & 4)
45 #define LOG_IO
46 #endif
47 //#define LOG_SEGV
48
49 #ifdef LOG_IO
50 #define iolog log_io
51 #else
52 #define iolog(...)
53 #endif
54
55 #ifdef LOG_IO_UNK
56 #define iolog_unh log_io
57 #else
58 #define iolog_unh(...)
59 #endif
60
61 #ifdef LOG_SEGV
62 #define segvlog g_printf
63 #else
64 #define segvlog(...)
65 #endif
66
67 #if defined(LOG_IO) || defined(LOG_IO_UNK)
68 #include "mmsp2-regs.h"
69 #endif
70
71 typedef unsigned long long u64;
72 typedef unsigned int   u32;
73 typedef unsigned short u16;
74 typedef unsigned char  u8;
75
76 #define THREAD_STACK_SIZE 0x200000
77
78 static int fb_sync_thread_paused;
79 static int fb_sync_thread_futex;
80
81 static int emu_is_dl;
82 static int emu_force_snd_frag;
83
84 static struct {
85   u32 dstctrl;
86   u32 dstaddr;
87   u32 dststride;
88   u32 srcctrl;
89   u32 srcaddr;          //
90   u32 srcstride;
91   u32 srcforcolor;
92   u32 srcbackcolor;
93   u32 patctrl;          //
94   u32 patforcolor;
95   u32 patbackcolor;
96   u32 size;
97   u32 ctrl;             //
98   u32 run;
99   u32 intc;
100   u32 srcfifo;
101 } blitter;
102
103 #define SRCCTRL_INVIDEO         (1 << 8)
104 #define SRCCTRL_SRCENB          (1 << 7)
105 #define CTRL_TRANSPARENCYENB    (1 << 11)
106
107 static struct {
108   // mmsp2
109   u16 mlc_stl_cntl;
110   union {
111     u32 mlc_stl_adr; // mlcaddress for pollux
112     struct {
113       u16 mlc_stl_adrl;
114       u16 mlc_stl_adrh;
115     };
116   };
117   u16 mlc_stl_pallt_a;
118   union {
119     u16 mlc_stl_pallt_d[256*2];
120     u32 mlc_stl_pallt_d32[256];
121   };
122
123   // pollux
124   u32 mlccontrol;
125   u16 mlcpalette[256];
126
127   // state
128   void *umem;
129   u32 old_mlc_stl_adr;
130   u32 btn_state; // as seen through /dev/GPIO: 0PVdVu YXBA RLSeSt 0Ri0Dn 0Le0Up
131   struct {
132     u32 width, height;
133     u32 stride;
134     u32 bpp;
135     u32 dirty_pal:2;
136   } v;
137 } mmsp2;
138 #define pollux mmsp2 // so that code doesn't look that weird
139 enum {
140   DIRTY_PAL_MMSP2 = 1,
141   DIRTY_PAL_POLLUX = 2,
142 };
143
144
145 #if defined(LOG_IO) || defined(LOG_IO_UNK)
146 static void log_io(const char *pfx, u32 a, u32 d, int size)
147 {
148   const char *fmt, *reg = "";
149   switch (size) {
150   case  8: fmt = "%s %08x       %02x %s\n"; d &= 0xff; break;
151   case 32: fmt = "%s %08x %08x %s\n";       break;
152   default: fmt = "%s %08x     %04x %s\n";   d &= 0xffff; break;
153   }
154
155   if ((a & ~0xffff) == 0x7f000000)
156     reg = regnames[a & 0xffff];
157
158   g_printf(fmt, pfx, a, d, reg);
159 }
160 #endif
161
162 static void memset16(void *dst, u32 pattern, int count)
163 {
164   u32 *dl;
165   u16 *d;
166   
167   d = (u16 *)((long)dst & ~1);
168   if ((long)d & 2) {
169     *d++ = pattern;
170     count--;
171   }
172   dl = (void *)d;
173   pattern |= pattern << 16;
174
175   while (count >= 2) {
176     *dl++ = pattern;
177     count -= 2;
178   }
179   if (count)
180     *(u16 *)dl = pattern;
181 }
182
183 static void blt_tr(void *dst, void *src, u32 trc, int w)
184 {
185   u16 *d = (u16 *)((long)dst & ~1);
186   u16 *s = (u16 *)((long)src & ~1);
187
188   // XXX: optimize
189   for (; w > 0; d++, s++, w--)
190     if (*s != trc)
191       *d = *s;
192 }
193
194 #define dump_blitter() \
195 { \
196   u32 *r = &blitter.dstctrl; \
197   int i; \
198   for (i = 0; i < 4*4; i++, r++) { \
199     g_printf("%08x ", *r); \
200     if ((i & 3) == 3) \
201       g_printf("\n"); \
202   } \
203 }
204
205 static void *uppermem_lookup(u32 addr, u8 **mem_end)
206 {
207   // XXX: maybe support mirroring?
208   if ((addr & 0xfe000000) != 0x02000000)
209     return NULL;
210
211   *mem_end = (u8 *)mmsp2.umem + 0x02000000;
212   return (u8 *)mmsp2.umem - 0x02000000 + addr;
213 }
214
215 static void blitter_do(void)
216 {
217   u8 *dst, *dste, *src = NULL, *srce = NULL;
218   int w, h, sstrd, dstrd;
219   int to_screen = 0;
220   u32 bpp, addr;
221
222   w = blitter.size & 0x7ff;
223   h = (blitter.size >> 16) & 0x7ff;
224   sstrd = blitter.srcstride;
225   dstrd = blitter.dststride;
226
227   // XXX: need to confirm this..
228   addr = (blitter.dstaddr & ~3) | ((blitter.dstctrl & 0x1f) >> 3);
229
230   // use dst bpp.. How does it do blits with different src bpp?
231   bpp = (blitter.dstctrl & 0x20) ? 16 : 8;
232
233   // maybe the screen?
234   if (((w == 320 && h == 240) || // blit whole screen
235        (w * h >= 320*240/2)) &&  // ..or at least half of the area
236        mmsp2.mlc_stl_adr <= addr && addr < mmsp2.mlc_stl_adr + 320*240*2)
237     to_screen = 1;
238
239   dst = uppermem_lookup(addr, &dste);
240
241   // XXX: assume fill if no SRCENB, but it could be pattern blit..
242   if (blitter.srcctrl & SRCCTRL_SRCENB) {
243     if (!(blitter.srcctrl & SRCCTRL_INVIDEO))
244       goto bad_blit;
245
246     addr = (blitter.srcaddr & ~3) | ((blitter.srcctrl & 0x1f) >> 3);
247     src = uppermem_lookup(addr, &srce);
248     if (src == NULL)
249       goto bad_blit;
250
251     if (src + sstrd * h > srce) {
252       err("blit %08x->%08x %dx%d did not fit src\n",
253         blitter.srcaddr, blitter.dstaddr, w, h);
254       h = (srce - src) / sstrd;
255     }
256   }
257
258   if (dst == NULL)
259     goto bad_blit;
260
261   if (dst + dstrd * h > dste) {
262     err("blit %08x->%08x %dx%d did not fit dst\n",
263       blitter.srcaddr, blitter.dstaddr, w, h);
264     h = (dste - dst) / dstrd;
265   }
266
267   if (src != NULL) {
268     // copy
269     if (bpp == 16 && (blitter.ctrl & CTRL_TRANSPARENCYENB)) {
270       u32 trc = blitter.ctrl >> 16;
271       for (; h > 0; h--, dst += dstrd, src += sstrd)
272         blt_tr(dst, src, trc, w);
273     }
274     else {
275       for (; h > 0; h--, dst += dstrd, src += sstrd)
276         memcpy(dst, src, w * bpp / 8);
277     }
278   }
279   else {
280     // fill. Assume the pattern is cleared and bg color is used
281     u32 bgc = blitter.patbackcolor & 0xffff;
282     if (bpp == 16) {
283       for (; h > 0; h--, dst += dstrd)
284         memset16(dst, bgc, w);
285     }
286     else {
287       for (; h > 0; h--, dst += dstrd)
288         memset(dst, bgc, w); // bgc?
289     }
290   }
291
292   if (to_screen) {
293     fb_sync_thread_futex = 1;
294     g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
295   }
296   return;
297
298 bad_blit:
299   err("blit %08x->%08x %dx%d translated to %p->%p\n",
300     blitter.srcaddr, blitter.dstaddr, w, h, src, dst);
301   dump_blitter();
302 }
303
304 // FIXME: pass real dimensions to blitters
305 static void mlc_flip(void *src, int bpp, int stride)
306 {
307   static int old_bpp;
308
309   // only pass pal to host if it's dirty
310   if (bpp <= 8 && mmsp2.v.dirty_pal) {
311     if (mmsp2.v.dirty_pal == DIRTY_PAL_MMSP2)
312       host_video_update_pal32(mmsp2.mlc_stl_pallt_d32);
313     else
314       host_video_update_pal16(mmsp2.mlcpalette);
315     mmsp2.v.dirty_pal = 0;
316   }
317
318   if (bpp != old_bpp) {
319     host_video_change_bpp(bpp);
320     old_bpp = bpp;
321   }
322
323   switch (bpp) {
324   case  4:
325     host_video_blit4(src, 320, 240, stride);
326     break;
327
328   case  8:
329     host_video_blit8(src, 320, 240, stride);
330     break;
331
332   case 16:
333     host_video_blit16(src, 320, 240, stride);
334     break;
335
336   case 24:
337     // TODO
338     break;
339   }
340 }
341
342 static void *fb_sync_thread(void *arg)
343 {
344   unsigned long sigmask[2] = { ~0ul, ~0ul };
345   struct timespec ts = { 0, 0 };
346   int invalid_fb_addr = 1;
347   int manual_refresh = 0;
348   int frame_counter = 0;
349   int wait_ret;
350
351   // this thread can't run any signal handlers since the
352   // app's stack/tls stuff will never be set up here
353   sigmask[0] &= ~(1ul << (SIGSEGV - 1));
354   g_rt_sigprocmask_raw(SIG_SETMASK, sigmask, NULL, sizeof(sigmask));
355
356   //ret = setpriority(PRIO_PROCESS, 0, -1);
357   //log("setpriority %d\n", ret);
358
359   // tell the main thread we're done init
360   fb_sync_thread_futex = 0;
361   g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
362
363   while (1) {
364     u8 *gp2x_fb, *gp2x_fb_end;
365
366     wait_ret = g_futex_raw(&fb_sync_thread_futex, FUTEX_WAIT, 0, &ts);
367
368     // this is supposed to be done atomically, but to make life
369     // easier ignore it for now, race impact is low anyway
370     fb_sync_thread_futex = 0;
371
372     if (wait_ret != 0 && wait_ret != -EWOULDBLOCK
373         && wait_ret != -ETIMEDOUT)
374     {
375       err("fb_thread: futex error: %d\n", wait_ret);
376       sleep(1);
377       goto check_keys;
378     }
379     if (fb_sync_thread_paused) {
380       ts.tv_nsec = 100000000;
381       goto check_keys;
382     }
383
384     if (wait_ret == 0) {
385       ts.tv_nsec = 50000000;
386       manual_refresh++;
387       if (manual_refresh == 2)
388         dbg("fb_thread: switch to manual refresh\n");
389     } else {
390       ts.tv_nsec = 16666667;
391       if (manual_refresh > 1)
392         dbg("fb_thread: switch to auto refresh\n");
393       manual_refresh = 0;
394     }
395
396     gp2x_fb = uppermem_lookup(mmsp2.mlc_stl_adr, &gp2x_fb_end);
397     if (gp2x_fb == NULL || gp2x_fb + 320*240 * mmsp2.v.bpp / 8 > gp2x_fb_end) {
398       if (!invalid_fb_addr) {
399         err("fb_thread: %08x is out of range\n", mmsp2.mlc_stl_adr);
400         invalid_fb_addr = 1;
401       }
402       continue;
403     }
404
405     invalid_fb_addr = 0;
406     mlc_flip(gp2x_fb, mmsp2.v.bpp, mmsp2.v.stride);
407
408     frame_counter++;
409     if (frame_counter & 0x0f)
410       continue;
411
412 check_keys:
413     // this is to check for kill key, in case main thread hung
414     // or something else went wrong.
415     pollux.btn_state = host_read_btns();
416   }
417 }
418
419 static void fb_thread_pause(void)
420 {
421   fb_sync_thread_paused = 1;
422   // wait until it finishes last refresh
423   // that it might be doing now
424   usleep(10000);
425 }
426
427 static void fb_thread_resume(void)
428 {
429   fb_sync_thread_paused = 0;
430 }
431
432 static u32 xread32_io_cmn(u32 a, u32 *handled)
433 {
434   struct timespec ts;
435   u32 d = 0;
436
437   *handled = 1;
438   switch (a) {
439   // Wiz stuff
440   case 0x1980: // TIMER3 TMRCOUNT
441     // assume the timer is set up for microsec time
442     g_clock_gettime_raw(CLOCK_REALTIME, &ts);
443     d = ts.tv_sec * 1000000 + ((u64)(u32)ts.tv_nsec * 4294968 >> 32);
444     break;
445   case 0x402c: // MLCVSTRIDE0
446   case 0x4060: // MLCVSTRIDE1
447     d = pollux.v.stride;
448     break;
449   case 0x4038: // MLCADDRESS0
450   case 0x406c: // MLCADDRESS1
451     d = pollux.mlc_stl_adr;
452     break;
453   // wiz_lib reads:
454   //  ???? ???? YXBA DURiLe ???? VdVuMS LR?? ????
455   // |     GPIOC[31:16]    |    GPIOB[31:16]     |
456   case 0xa058: // GPIOBPAD
457     d =  (pollux.btn_state >> 1) & 0x0100;
458     d |= (pollux.btn_state << 1) & 0x0200;
459     d |= (pollux.btn_state >> 3) & 0x0080;
460     d |= (pollux.btn_state >> 5) & 0x0040;
461     d |= (pollux.btn_state >> 6) & 0x0c00;
462     d <<= 16;
463     d = ~d;
464     break;
465   case 0xa098: // GPIOCPAD
466     pollux.btn_state = host_read_btns();
467     d =  (pollux.btn_state >> 8) & 0x00f0;
468     d |= (pollux.btn_state >> 1) & 0x0008;
469     d |= (pollux.btn_state << 2) & 0x0004;
470     d |= (pollux.btn_state >> 5) & 0x0002;
471     d |= (pollux.btn_state >> 2) & 0x0001;
472     d <<= 16;
473     d = ~d;
474     break;
475   default:
476     *handled = 0;
477     break;
478   }
479
480   return d;
481 }
482
483 static u32 xread8(u32 a)
484 {
485   iolog("r8 ", a, 0, 8);
486   iolog_unh("r8 ", a, 0, 8);
487   return 0;
488 }
489
490 static u32 xread16(u32 a)
491 {
492   static u32 fudge, old_a;
493   u32 d = 0, t;
494
495   if ((a & 0xffff0000) == 0x7f000000) {
496     u32 a_ = a & 0xffff;
497     switch (a_) {
498     case 0x0910: // FPLL
499     case 0x0912:
500       d = 0x9407;
501       break;
502     // minilib reads as:
503     //  0000 P000 VuVd00 0000 YXBA RLSeSt 0Ri0D 0Le0U
504     // |        GPIOD        |GPIOC[8:15]|GPIOM[0:7] |
505     // /dev/GPIO:
506     //             ... 0PVdVu ...
507     case 0x1184: // GPIOC
508       d = ~mmsp2.btn_state & 0xff00;
509       d |= 0x00ff;
510       break;
511     case 0x1186: // GPIOD
512       t = ~mmsp2.btn_state;
513       d  = (t >> 9)  & 0x0080;
514       d |= (t >> 11) & 0x0040;
515       d |= (t >> 7)  & 0x0800;
516       d |= 0x373b;
517       break;
518     case 0x1198: // GPIOM
519       mmsp2.btn_state = host_read_btns();
520       d = ~mmsp2.btn_state & 0xff;
521       d |= 0x01aa;
522       break;
523     case 0x1836: // reserved
524       d = 0x2330;
525       break;
526     case 0x2816: // DPC_X_MAX
527       d = 319;
528       break;
529     case 0x2818: // DPC_Y_MAX
530       d = 239;
531       break;
532     case 0x28da:
533       d = mmsp2.mlc_stl_cntl;
534       break;
535     case 0x290e:
536     case 0x2912:
537       d = mmsp2.mlc_stl_adrl;
538       break;
539     case 0x2910:
540     case 0x2914:
541       d = mmsp2.mlc_stl_adrh;
542       break;
543     case 0x2958:
544       d = mmsp2.mlc_stl_pallt_a;
545       break;
546
547     default:
548       d = xread32_io_cmn(a_, &t);
549       if (!t)
550         goto unk;
551       if (!(a_ & 2))
552         d >>= 16;
553       break;
554     }
555     goto out;
556   }
557
558 unk:
559   if (a == old_a) {
560     d = fudge;
561     fudge = ~fudge;
562   }
563   old_a = a;
564   iolog_unh("r16", a, d & 0xffff, 16);
565
566 out:
567   d &= 0xffff;
568   iolog("r16", a, d, 16);
569   return d;
570 }
571
572 static u32 xread32(u32 a)
573 {
574   u32 d = 0;
575   if ((a & 0xfff00000) == 0x7f000000) {
576     u32 a_ = a & 0xffff;
577     struct timespec ts;
578     u64 t64;
579     u32 t;
580
581     switch (a_) {
582     case 0x0a00: // TCOUNT, 1/7372800s
583       g_clock_gettime_raw(CLOCK_REALTIME, &ts);
584       t64 = (u64)ts.tv_sec * 1000000000 + ts.tv_nsec;
585       // t * 7372800.0 / 1000000000 * 0x100000000 ~= t * 31665935
586       t64 *= 31665935;
587       d = t64 >> 32;
588       break;
589
590     default:
591       d = xread32_io_cmn(a_, &t);
592       if (!t)
593         goto unh;
594       break;
595     }
596     goto out;
597   }
598   if ((a & 0xfff00000) == 0x7f100000) {
599     u32 *bl = &blitter.dstctrl;
600     u32 a_ = a & 0xfff;
601     if (a_ < 0x40) {
602       d = bl[a_ / 4];
603       if (a_ == 0x34)
604         d = 0; // not busy
605       goto out;
606     }
607   }
608
609 unh:
610   iolog_unh("r32", a, d, 32);
611
612 out:
613   iolog("r32", a, d, 32);
614   return d;
615 }
616
617 static void xwrite8(u32 a, u32 d)
618 {
619   iolog("w8 ", a, d, 8);
620   iolog_unh("w8 ", a, d, 8);
621 }
622
623 static void xwrite16(u32 a, u32 d)
624 {
625   iolog("w16", a, d, 16);
626   if ((a & 0xfff00000) == 0x7f000000) {
627     u32 a_ = a & 0xffff;
628     switch (a_) {
629       case 0x28da: {
630         int mode;
631         mmsp2.mlc_stl_cntl = d | 0xaa;
632         mode = (d >> 9) & 3;
633         mmsp2.v.bpp = mode ? mode * 8 : 4;
634         break;
635       }
636       case 0x290c:
637         mmsp2.v.stride = d;
638         return;
639       case 0x290e:
640       case 0x2910:
641         // odd addresses don't affect LCD. What about TV?
642         return;
643       case 0x2912:
644         mmsp2.mlc_stl_adrl = d;
645         return;
646       case 0x2914:
647         mmsp2.mlc_stl_adrh = d;
648         if (mmsp2.mlc_stl_adr != mmsp2.old_mlc_stl_adr) {
649           // ask for refresh
650           fb_sync_thread_futex = 1;
651           g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
652         }
653         mmsp2.old_mlc_stl_adr = mmsp2.mlc_stl_adr;
654         return;
655       case 0x2958: // MLC_STL_PALLT_A
656         mmsp2.mlc_stl_pallt_a = d & 0x1ff;
657         return;
658       case 0x295a: // MLC_STL_PALLT_D
659         mmsp2.mlc_stl_pallt_d[mmsp2.mlc_stl_pallt_a++] = d;
660         mmsp2.mlc_stl_pallt_a &= 0x1ff;
661         mmsp2.v.dirty_pal = DIRTY_PAL_MMSP2;
662         return;
663     }
664   }
665   iolog_unh("w16", a, d, 16);
666 }
667
668 static void xwrite32(u32 a, u32 d)
669 {
670   iolog("w32", a, d, 32);
671
672   if ((a & 0xfff00000) == 0x7f000000) {
673     u32 a_ = a & 0xffff;
674     switch (a_) {
675     // GP2X
676     case 0x295a: // MLC_STL_PALLT_D
677       // special unaligned 32bit write, allegro seems to rely on it
678       mmsp2.mlc_stl_pallt_d[mmsp2.mlc_stl_pallt_a++ & 0x1ff] = d;
679       mmsp2.mlc_stl_pallt_d[mmsp2.mlc_stl_pallt_a++ & 0x1ff] = d >> 16;
680       mmsp2.mlc_stl_pallt_a &= 0x1ff;
681       mmsp2.v.dirty_pal = DIRTY_PAL_MMSP2;
682       return;
683     // Wiz
684     case 0x4024: // MLCCONTROL0
685     case 0x4058: // MLCCONTROL1
686       pollux.mlccontrol = d;
687       if (!(d & 0x20))
688         return; // layer not enabled
689       if ((d >> 16) == 0x443A)
690         pollux.v.bpp = 8;
691       else
692         pollux.v.bpp = 16;
693       return;
694     case 0x402c: // MLCVSTRIDE0
695     case 0x4060: // MLCVSTRIDE1
696       pollux.v.stride = d;
697       return;
698     case 0x4038: // MLCADDRESS0
699     case 0x406c: // MLCADDRESS1
700       pollux.mlc_stl_adr = d;
701       if (d != mmsp2.old_mlc_stl_adr) {
702         // ask for refresh
703         fb_sync_thread_futex = 1;
704         g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
705       }
706       mmsp2.old_mlc_stl_adr = d;
707       return;
708     case 0x403c: // MLCPALETTE0
709     case 0x4070: // MLCPALETTE1
710       pollux.mlcpalette[d >> 24] = d;
711       pollux.v.dirty_pal = DIRTY_PAL_POLLUX;
712       return;
713     }
714   }
715   if ((a & 0xfff00000) == 0x7f100000) {
716     u32 *bl = &blitter.dstctrl;
717     u32 a_ = a & 0xfff;
718     if (a_ < 0x40) {
719       bl[a_ / 4] = d;
720       if (a_ == 0x34 && (d & 1))
721         blitter_do();
722       return;
723     }
724   }
725   iolog_unh("w32", a, d, 32);
726 }
727
728 #define LINKPAGE_SIZE 0x1000
729 #define LINKPAGE_COUNT 4
730 #define LINKPAGE_ALLOC (LINKPAGE_SIZE * LINKPAGE_COUNT)
731
732 struct op_context {
733   u32 pc;
734   u32 op;
735   u32 code[0];
736 };
737
738 struct op_linkpage {
739   u32 *code_ptr;
740   void (*handler)(struct op_context *op_ctx);
741   u32 code[0];
742 };
743
744 struct op_stackframe {
745   u32 saved_regs[15];
746   u32 cpsr;
747 };
748
749 static struct op_linkpage *g_linkpages[2];
750 static int g_linkpage_count;
751
752 enum opcond {
753   C_EQ, C_NE, C_CS, C_CC, C_MI, C_PL, C_VS, C_VC,
754   C_HI, C_LS, C_GE, C_LT, C_GT, C_LE, C_AL,
755 };
756 enum cpsr_cond {
757   CPSR_N = (1u << 31),
758   CPSR_Z = (1u << 30),
759   CPSR_C = (1u << 29),
760   CPSR_V = (1u << 28),
761 };
762
763 #define BIT_SET(v, b) (v & (1 << (b)))
764
765 void emu_handle_op(struct op_context *op_ctx, struct op_stackframe *sframe)
766 {
767   u32 *regs = sframe->saved_regs;
768   u32 cpsr = sframe->cpsr;
769   u32 op = op_ctx->op;
770   u32 t, shift, ret, addr;
771   int i, rn, rd, cond;
772
773   cond = (op & 0xf0000000) >> 28;
774   rd = (op & 0x0000f000) >> 12;
775   rn = (op & 0x000f0000) >> 16;
776
777   if (cond != 0x0e) {
778     switch (cond) {
779     case C_EQ: if ( (cpsr & CPSR_Z)) break; return;
780     case C_NE: if (!(cpsr & CPSR_Z)) break; return;
781     case C_CS: if ( (cpsr & CPSR_C)) break; return;
782     case C_CC: if (!(cpsr & CPSR_C)) break; return;
783     case C_MI: if ( (cpsr & CPSR_N)) break; return;
784     case C_PL: if (!(cpsr & CPSR_N)) break; return;
785     case C_VS: if ( (cpsr & CPSR_V)) break; return;
786     case C_VC: if (!(cpsr & CPSR_V)) break; return;
787     default:
788       goto unhandled;
789     }
790   }
791
792   if ((op & 0x0f200090) == 0x01000090) { // AM3: LDRH, STRH
793     if (!BIT_SET(op, 5)) // !H
794       goto unhandled;
795     if (BIT_SET(op, 6) && !BIT_SET(op, 20)) // S && !L
796       goto unhandled;
797
798     if (BIT_SET(op, 22))                // imm offset
799       t = ((op & 0xf00) >> 4) | (op & 0x0f);
800     else                                // reg offset
801       t = regs[op & 0x000f];
802
803     if (!BIT_SET(op, 23))
804       t = -t;
805     addr = regs[rn] + t;
806
807     if (BIT_SET(op, 20)) { // Load
808       ret = xread16(addr);
809       if (BIT_SET(op, 6)) { // S
810         ret <<= 16;
811         ret = (signed int)ret >> 16;
812       }
813       regs[rd] = ret;
814     }
815     else
816       xwrite16(addr, regs[rd]);
817   }
818   else if ((op & 0x0c000000) == 0x04000000) { // load/store word/byte
819     if (BIT_SET(op, 21))
820       goto unhandled;                   // unprivileged
821     if (BIT_SET(op, 25)) {              // reg offs
822       if (BIT_SET(op, 4))
823         goto unhandled;                 // nah it's media
824
825       t = regs[op & 0x000f];
826       shift = (op & 0x0f80) >> 7;
827       switch ((op & 0x0060) >> 5) {
828         case 0: t = t << shift; break;
829         case 1: t = t >> (shift + 1); break;
830         case 2: t = (signed int)t >> (shift + 1); break;
831         case 3: goto unhandled; // I'm just lazy
832       }
833     }
834     else                                // imm offs
835       t = op & 0x0fff;
836
837     if (!BIT_SET(op, 23))
838       t = -t;
839
840     addr = regs[rn];
841     if (BIT_SET(op, 24))   // pre-indexed
842       addr += t;
843     if (!BIT_SET(op, 24) || BIT_SET(op, 21))
844       regs[rn] += t;       // writeback
845
846     if (BIT_SET(op, 20)) { // Load
847       if (BIT_SET(op, 22)) // Byte
848         ret = xread8(addr);
849       else
850         ret = xread32(addr);
851       regs[rd] = ret;
852     }
853     else {
854       if (BIT_SET(op, 22)) // Byte
855         xwrite8(addr, regs[rd]);
856       else
857         xwrite32(addr, regs[rd]);
858     }
859   }
860   else
861     goto unhandled;
862
863 #if 0
864   if (addr != addr_check) {
865     fprintf(stderr, "bad calculated addr: %08x vs %08x\n", addr, addr_check);
866     abort();
867   }
868 #endif
869   return;
870
871 unhandled:
872   err("unhandled IO op %08x @ %08x\n", op, op_ctx->pc);
873   for (i = 0; i < 8-1; i++)
874     err(" r%d=%08x  r%-2d=%08x\n", i, regs[i], i+8, regs[i+8]);
875   err(" r%d=%08x cpsr=%08x\n", i, regs[i], cpsr);
876   abort();
877 }
878
879 static u32 make_offset12(u32 *pc, u32 *target)
880 {
881   int lp_offs, u = 1;
882
883   lp_offs = (char *)target - (char *)pc - 2*4;
884   if (lp_offs < 0) {
885     lp_offs = -lp_offs;
886     u = 0;
887   }
888   if (lp_offs >= LINKPAGE_SIZE) {
889     err("linkpage too far: %d\n", lp_offs);
890     abort();
891   }
892
893   return (u << 23) | lp_offs;
894 }
895
896 static u32 make_jmp(u32 *pc, u32 *target, int bl)
897 {
898   int jmp_val;
899
900   jmp_val = target - pc - 2;
901   if (jmp_val < (int)0xff800000 || jmp_val > 0x007fffff) {
902     err("jump out of range (%p -> %p)\n", pc, target);
903     abort();
904   }
905
906   return 0xea000000 | (bl << 24) | (jmp_val & 0x00ffffff);
907 }
908
909 static void emit_op(struct op_linkpage *linkpage, u32 op)
910 {
911   *linkpage->code_ptr++ = op;
912 }
913
914 static void emit_op_io(struct op_linkpage *linkpage,
915   u32 op, u32 *target)
916 {
917   op |= make_offset12(linkpage->code_ptr, target);
918   emit_op(linkpage, op);
919 }
920
921 static void init_linkpage(struct op_linkpage *linkpage)
922 {
923   linkpage->handler = emu_call_handle_op;
924   linkpage->code_ptr = linkpage->code;
925 }
926
927 static void segv_sigaction(int num, siginfo_t *info, void *ctx)
928 {
929   extern char _init, _end;
930   struct ucontext *context = ctx;
931   u32 *regs = (u32 *)&context->uc_mcontext.arm_r0;
932   u32 *pc = (u32 *)regs[15];
933   u32 self_start, self_end;
934   struct op_linkpage *lp = NULL;
935   struct op_context *op_ctx;
936   int i, ret, lp_i, lp_size;
937
938   self_start = (u32)&_init & ~0xfff;
939   self_end = (u32)&_end;
940
941   if ((self_start <= regs[15] && regs[15] <= self_end) ||           // PC is in our segment or
942      !((regs[15] ^ (u32)g_linkpages[0]) & ~(LINKPAGE_ALLOC - 1)) || // .. in linkpage
943       ((long)info->si_addr & 0xffe00000) != 0x7f000000)             // faulting not where expected
944   {
945     // real crash - time to die
946     err("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]);
947     for (i = 0; i < 8; i++)
948       dbg(" r%d=%08x r%-2d=%08x\n", i, regs[i], i+8, regs[i+8]);
949     signal(num, SIG_DFL);
950     raise(num);
951     return;
952   }
953   segvlog("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]);
954
955   // find nearby linkpage
956   for (lp_i = 0; lp_i < ARRAY_SIZE(g_linkpages); lp_i++) {
957     if (g_linkpages[lp_i] == NULL)
958       continue;
959     i = g_linkpages[lp_i]->code_ptr + 2 - pc - 2;
960     if ((int)0xff800000 <= i && i <= 0x007fffff) {
961       lp = g_linkpages[lp_i];
962       break;
963     }
964   }
965
966   if (lp == NULL) {
967     err("fatal: no nearby linkpage for %08x\n", regs[15]);
968     abort();
969   }
970
971   if (emu_is_dl) {
972     ret = mprotect((void *)((long)pc & ~0xfff), 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC);
973     if (ret != 0)
974       perror("warning: mprotect");
975   }
976
977   // spit PC and op
978   op_ctx = (void *)lp->code_ptr;
979   op_ctx->pc = (u32)pc;
980   op_ctx->op = *pc;
981   lp->code_ptr = &op_ctx->code[0];
982
983   // emit jump to code ptr
984   *pc = make_jmp(pc, lp->code_ptr, 0);
985
986   // generate code:
987   emit_op   (lp, 0xe50d0000 + 0xf00 - 4 * 0);                        // str r0, [sp, #(-0xf00 + r0_offs)]
988   emit_op   (lp, 0xe50de000 + 0xf00 - 4 * 14);                       // str lr, [sp, #(-0xf00 + lr_offs)]
989   emit_op   (lp, 0xe24f0000 + (lp->code_ptr - (u32 *)op_ctx + 2) * 4); // sub r0, pc, #op_ctx
990   emit_op   (lp, 0xe1a0e00f);                                        // mov lr, pc
991   emit_op_io(lp, 0xe51ff000, (u32 *)&lp->handler);                   // ldr pc, =handle_op
992   emit_op   (lp, 0xe51de000 + 0xf00 - 4 * 14);                       // ldr lr, [sp, #(-0xf00 + lr_offs)]
993   emit_op   (lp, make_jmp(lp->code_ptr, pc + 1, 0));                 // jmp <back>
994
995   // sync caches
996   sys_cacheflush(pc, pc + 1);
997   sys_cacheflush(lp, lp->code_ptr);
998
999   lp_size = (char *)lp->code_ptr - (char *)lp;
1000   segvlog("code #%d %d/%d\n", g_linkpage_count, lp_size, LINKPAGE_SIZE);
1001
1002   if (lp_size + 14*4 > LINKPAGE_SIZE) {
1003     g_linkpage_count++;
1004     if (g_linkpage_count >= LINKPAGE_COUNT) {
1005       err("too many linkpages needed\n");
1006       abort();
1007     }
1008     g_linkpages[lp_i] = (void *)((char *)g_linkpages[lp_i] + LINKPAGE_SIZE);
1009     init_linkpage(g_linkpages[lp_i]);
1010   }
1011   //handle_op(regs[15], op, regs, (u32)info->si_addr);
1012   //regs[15] += 4;
1013 }
1014
1015 void emu_init(void *map_bottom[2], int is_dl)
1016 {
1017   sigaction_t segv_action = {
1018     .sa_sigaction = segv_sigaction,
1019     .sa_flags = SA_SIGINFO,
1020   };
1021   const char *var;
1022   void *pret;
1023   int i, ret;
1024
1025 #ifdef PND
1026   if (geteuid() == 0) {
1027     err("don't try to run as root, device registers or memory "
1028         "might get trashed crashing the OS or even damaging the device.\n");
1029     exit(1);
1030   }
1031 #endif
1032
1033   emu_is_dl = is_dl;
1034
1035 #ifdef PND
1036   // set default buffer size to 8 * 1K
1037   emu_force_snd_frag = (8<<16) | 10;
1038 #endif
1039   var = getenv("GINGE_SETFRAGMENT");
1040   if (var != NULL)
1041     emu_force_snd_frag = strtol(var, NULL, 0);
1042
1043   for (i = 0; i < 2; i++) {
1044     if (map_bottom[i] == NULL)
1045       continue;
1046     g_linkpages[i] = (void *)(((u32)map_bottom[i] - LINKPAGE_ALLOC) & ~0xfff);
1047     pret = mmap(g_linkpages[i], LINKPAGE_ALLOC, PROT_READ|PROT_WRITE,
1048                 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
1049     if (pret != g_linkpages[i]) {
1050       err("linkpage alloc @ %p: ", g_linkpages[i]);
1051       perror(NULL);
1052       exit(1);
1053     }
1054     log("linkpages @ %p\n", g_linkpages[i]);
1055     init_linkpage(g_linkpages[i]);
1056   }
1057
1058   // host stuff
1059   ret = host_init();
1060   if (ret != 0) {
1061     err("can't init host\n");
1062     exit(1);
1063   }
1064
1065   ret = host_video_init(NULL, 0);
1066   if (ret != 0) {
1067     err("can't init host video\n");
1068     exit(1);
1069   }
1070
1071   // TODO: check if this really fails on Wiz..
1072   mmsp2.umem = mmap(NULL, 0x2000000, PROT_READ|PROT_WRITE|PROT_EXEC,
1073                     MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
1074 #ifdef WIZ
1075   if (mmsp2.umem == MAP_FAILED) {
1076     // we are short on memmory on Wiz, need special handling
1077     extern void *host_mmap_upper(void);
1078     mmsp2.umem = host_mmap_upper();
1079   }
1080 #endif
1081   if (mmsp2.umem == MAP_FAILED) {
1082     perror(PFX "mmap upper mem");
1083     exit(1);
1084   }
1085
1086   pret = mmap(NULL, THREAD_STACK_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC,
1087               MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0);
1088   if (mmsp2.umem == MAP_FAILED) {
1089     perror(PFX "mmap thread stack");
1090     exit(1);
1091   }
1092   fb_sync_thread_futex = 1;
1093   ret = g_clone(CLONE_VM | CLONE_FS | CLONE_FILES
1094                 | CLONE_SIGHAND | CLONE_THREAD,
1095                 (char *)pret + THREAD_STACK_SIZE, 0, 0, 0,
1096                 fb_sync_thread);
1097   if (ret == 0 || ret == -1) {
1098     perror(PFX "start fb thread");
1099     exit(1);
1100   }
1101   g_futex_raw(&fb_sync_thread_futex, FUTEX_WAIT, 1, NULL);
1102
1103   // defaults
1104   mmsp2.mlc_stl_adr = 0x03101000; // fb2 is at 0x03381000
1105   mmsp2.mlc_stl_cntl = 0x4ab; // 16bpp, region 1 active
1106   mmsp2.v.width = 320;
1107   mmsp2.v.height = 240;
1108   mmsp2.v.stride = 320*2;
1109   mmsp2.v.bpp = 16;
1110   mmsp2.v.dirty_pal = 1;
1111
1112   sigemptyset(&segv_action.sa_mask);
1113   sigaction(SIGSEGV, &segv_action, NULL);
1114 }
1115
1116 static long emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset)
1117 {
1118   u8 *umem, *umem_end;
1119
1120   // SoC regs
1121   if ((offset & ~0x1ffff) == 0xc0000000) {
1122     return g_mmap2_raw((void *)0x7f000000, length, PROT_NONE,
1123       MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0);
1124   }
1125   // MMSP2 blitter
1126   if ((offset & ~0xffff) == 0xe0020000) {
1127     return g_mmap2_raw((void *)0x7f100000, length, PROT_NONE,
1128       MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0);
1129   }
1130   // upper mem
1131   if ((offset & 0xfe000000) != 0x02000000) {
1132     err("unexpected devmem mmap @ %08x\n", offset);
1133     return -EINVAL;
1134   }
1135
1136   umem = uppermem_lookup(offset, &umem_end);
1137   if (umem + length > umem_end)
1138     err("warning: uppermem @ %08x overflows by %d bytes\n",
1139         offset, umem + length - umem_end);
1140
1141   dbg("upper mem @ %08x %x = %p\n", offset, length, umem);
1142   return (long)umem;
1143 }
1144
1145 long emu_do_mmap(unsigned int length, int prot, int flags, int fd,
1146   unsigned int offset)
1147 {
1148   if (fd == FAKEDEV_MEM)
1149     return emu_mmap_dev(length, prot, flags, offset);
1150
1151   if (fd == FAKEDEV_FB0)
1152     return emu_mmap_dev(length, prot, flags, offset + 0x03101000);
1153
1154   if (fd == FAKEDEV_FB1)
1155     return emu_mmap_dev(length, prot, flags, offset + 0x03381000);
1156
1157   err("bad/ni mmap(?, %d, %x, %x, %d, %08x)\n", length, prot, flags, fd, offset);
1158   return -EINVAL;
1159 }
1160
1161 long emu_do_munmap(void *addr, unsigned int length)
1162 {
1163   u8 *p = addr;
1164
1165   // don't allow to unmap upper mem
1166   if ((u8 *)mmsp2.umem <= p && p < (u8 *)mmsp2.umem + 0x2000000) {
1167     dbg("ignoring munmap: %p %x\n", addr, length);
1168     return 0;
1169   }
1170
1171   return -EAGAIN;
1172 }
1173
1174 static void emu_sound_open(int fd)
1175 {
1176   int ret;
1177
1178   if (emu_force_snd_frag != 0) {
1179     ret = g_ioctl_raw(fd, SNDCTL_DSP_SETFRAGMENT, &emu_force_snd_frag);
1180     if (ret != 0)
1181       err("snd ioctl SETFRAGMENT %08x: %d\n", emu_force_snd_frag, ret);
1182   }
1183 }
1184
1185 static long emu_sound_ioctl(int fd, int request, void *argp)
1186 {
1187   int *arg = argp;
1188
1189 #if 0
1190   dbg("snd ioctl(%d, %08x, %p)", fd, request, argp);
1191   if (arg != NULL)
1192     dbg_c(" [%d]", *arg);
1193   dbg_c("\n");
1194 #endif
1195
1196   /* People set strange frag settings on GP2X, which even manage
1197    * to break audio on pandora (causes writes to fail).
1198    * Catch this and set to something that works. */
1199   switch(request) {
1200     case SNDCTL_DSP_SETFRAGMENT: {
1201       int bsize, frag, frag_cnt;
1202       long ret;
1203
1204       if (arg == NULL)
1205         break;
1206
1207       frag = *arg & 0xffff;
1208       frag_cnt = *arg >> 16;
1209       bsize = frag_cnt << frag;
1210       if (frag < 8 || bsize < 4096*4 || bsize > 4096*4*2) {
1211         /*
1212          * ~4ms. gpSP wants small buffers or else it stutters
1213          * because of it's audio thread sync stuff
1214          * XXX: hardcoding, as low samplerates will result in small fragment size,
1215          * which itself causes ALSA stall and hangs the program.
1216          * Also some apps change samplerate without reopening /dev/dsp,
1217          * which causes ALSA to reject SNDCTL_DSP_SETFRAGMENT.
1218          */
1219         bsize = 44100 / 250 * 4;
1220
1221         for (frag = 0; bsize; bsize >>= 1, frag++)
1222           ;
1223
1224         frag_cnt = 8;
1225       }
1226
1227       frag |= frag_cnt << 16;
1228       ret = g_ioctl_raw(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
1229       if (ret != 0)
1230         err("snd ioctl SETFRAGMENT %08x: %ld\n", frag, ret);
1231       // indicate success even if we fail (because of ALSA mostly),
1232       // things like MikMod will bail out otherwise.
1233       return 0;
1234     }
1235     case SNDCTL_DSP_SYNC:
1236       // Franxis tends to use sync/write loops, bad idea under ALSA
1237       return 0;
1238     default:
1239       break;
1240   }
1241
1242   return g_ioctl_raw(fd, request, argp);
1243 }
1244
1245 long emu_do_ioctl(int fd, int request, void *argp)
1246 {
1247   if (fd == emu_interesting_fds[IFD_SOUND].fd)
1248     return emu_sound_ioctl(fd, request, argp);
1249
1250   switch (fd) {
1251   /* *********************** */
1252   case FAKEDEV_FB0:
1253   case FAKEDEV_FB1:
1254     if (argp == NULL)
1255       goto fail;
1256
1257     switch (request) {
1258       case FBIOGET_FSCREENINFO: {
1259         struct fb_fix_screeninfo *fix = argp;
1260
1261         memset(fix, 0, sizeof(*fix));
1262         strcpy(fix->id, "mmsp2_RGB0");
1263         fix->type         = FB_TYPE_PACKED_PIXELS;
1264         fix->accel        = FB_ACCEL_NONE;
1265         fix->visual       = FB_VISUAL_TRUECOLOR;
1266         fix->line_length  = 320*2;
1267         fix->smem_start   = (fd == FAKEDEV_FB0) ? 0x03101000 : 0x03381000;
1268         fix->smem_len     = 320*240*2;
1269         return 0;
1270       }
1271       case FBIOGET_VSCREENINFO: {
1272         struct fb_var_screeninfo *var = argp;
1273         static const struct fb_bitfield fbb_red   = { offset: 11, length: 5, };
1274         static const struct fb_bitfield fbb_green = { offset:  5, length: 6, };
1275         static const struct fb_bitfield fbb_blue  = { offset:  0, length: 5, };
1276
1277         memset(var, 0, sizeof(*var));
1278         var->activate     = FB_ACTIVATE_NOW;
1279         var->xres         =
1280         var->xres_virtual = 320;
1281         var->yres         =
1282         var->yres_virtual = 240;
1283         var->width        =
1284         var->height       = -1;
1285         var->vmode        = FB_VMODE_NONINTERLACED;
1286         var->bits_per_pixel = 16;
1287         var->red          = fbb_red;
1288         var->green        = fbb_green;
1289         var->blue         = fbb_blue;
1290         return 0;
1291       }
1292       case FBIOPUT_VSCREENINFO: {
1293         struct fb_var_screeninfo *var = argp;
1294         dbg(" put vscreen: %dx%d@%d\n", var->xres, var->yres, var->bits_per_pixel);
1295         if (var->xres != 320 || var->yres != 240 || var->bits_per_pixel != 16)
1296           return -1;
1297         return 0;
1298       }
1299     }
1300
1301   /* *********************** */
1302   case FAKEDEV_TTY:
1303   case FAKEDEV_TTY0:
1304     // fake tty0 to make GPH SDL happy
1305     if (request == 0x4b46) // KDGKBENT
1306       return -1;
1307     return 0;
1308   }
1309
1310 fail:
1311   err("bad/ni ioctl(%d, %08x, %p)\n", fd, request, argp);
1312   return -EINVAL;
1313 }
1314
1315 static const char wm97xx_p[] =
1316   "5507 0 -831476 0 -4218 16450692 65536"; // from 4.0 fw
1317
1318 long emu_do_read(int fd, void *buf, int count)
1319 {
1320   int ret, pressed = 0, x, y;
1321   struct {
1322     u16 pressure, x, y;
1323   } wm97xx;
1324
1325   if (count < 0) {
1326     err("read(%d, %d)\n", fd, count);
1327     return -EINVAL;
1328   }
1329
1330   switch (fd) {
1331   case FAKEDEV_GPIO:
1332     mmsp2.btn_state = host_read_btns();
1333
1334     if (count > 4)
1335       count = 4;
1336     memcpy(buf, &mmsp2.btn_state, count);
1337     break;
1338   case FAKEDEV_WM97XX:
1339     ret = host_read_ts(&pressed, &x, &y);
1340     if (ret == 0 && pressed) {
1341       wm97xx.pressure = 0x8001; // TODO: check the real thing
1342       wm97xx.x =        x * 3750 / 1024 + 200;
1343       wm97xx.y = 3750 - y * 3750 / 1024 + 200;
1344     }
1345     else {
1346       wm97xx.pressure = 0;
1347       wm97xx.x = wm97xx.y = 200;
1348     }
1349
1350     if (count > sizeof(wm97xx))
1351       count = sizeof(wm97xx);
1352     memcpy(buf, &wm97xx, count);
1353     break;
1354   case FAKEDEV_WM97XX_P:
1355     if (count < sizeof(wm97xx_p))
1356       err("incomplete pointercal read\n");
1357     strncpy(buf, wm97xx_p, count);
1358     break;
1359   default:
1360     dbg("read(%d, %d)\n", fd, count);
1361     return -EINVAL;
1362   }
1363   return count;
1364 }
1365
1366 struct dev_fd_t emu_interesting_fds[] = {
1367   [IFD_SOUND] = { "/dev/dsp", -1, emu_sound_open },
1368   { NULL, 0, NULL },
1369 };
1370
1371 static const struct {
1372   const char *from;
1373   const char *to;
1374 } path_map[] = {
1375   { "/mnt/tmp", "./tmp" },
1376   { "/mnt/sd", "./mntsd" },
1377 };
1378
1379 const char *emu_wrap_path(const char *path)
1380 {
1381   char *buff, *p;
1382   size_t size;
1383   int i, len;
1384   long ret;
1385
1386   // do only path mapping for now
1387   for (i = 0; i < ARRAY_SIZE(path_map); i++) {
1388     p = strstr(path, path_map[i].from);
1389     if (p != NULL) {
1390       size = strlen(path) + strlen(path_map[i].to) + 1;
1391       buff = malloc(size);
1392       if (buff == NULL)
1393         break;
1394       len = p - path;
1395       strncpy(buff, path, len);
1396       snprintf(buff + len, size - len, "%s%s", path_map[i].to,
1397         path + len + strlen(path_map[i].from));
1398       dbg("mapped path \"%s\" -> \"%s\"\n", path, buff);
1399
1400       ret = g_mkdir_raw(path_map[i].to, 0666);
1401       if (ret != 0 && ret != -EEXIST)
1402         err("mkdir(%s): %ld\n", path_map[i].to, ret);
1403
1404       return buff;
1405     }
1406   }
1407
1408   return path;
1409 }
1410
1411 void emu_wrap_path_free(const char *w_path, const char *old_path)
1412 {
1413   if (w_path != old_path)
1414     free((void *)w_path);
1415 }
1416
1417 void *emu_do_fopen(const char *path, const char *mode)
1418 {
1419   const char *w_path;
1420   FILE *ret;
1421
1422   if (strcmp(path, "/etc/pointercal") == 0) {
1423     // use local pontercal, not host's
1424     ret = fopen("pointercal", mode);
1425     if (ret == NULL) {
1426       ret = fopen("pointercal", "w");
1427       if (ret != NULL) {
1428         fwrite(wm97xx_p, 1, sizeof(wm97xx_p), ret);
1429         fclose(ret);
1430       }
1431       ret = fopen("pointercal", mode);
1432     }
1433   }
1434   else {
1435     w_path = emu_wrap_path(path);
1436     ret = fopen(w_path, mode);
1437     emu_wrap_path_free(w_path, path);
1438   }
1439
1440   return ret;
1441 }
1442
1443 // FIXME: threads..
1444 int emu_do_system(const char *command)
1445 {
1446   static char tmp_path[512];
1447   int need_ginge = 0;
1448   const char *p2;
1449   char *p;
1450   int ret;
1451
1452   if (command == NULL)
1453     return -1;
1454
1455   for (p2 = command; *p2 && isspace(*p2); p2++)
1456     ;
1457
1458   if (*p2 == '.') // relative path?
1459     need_ginge = 1;
1460   else if (*p2 == '/' && strncmp(p2, "/bin", 4) && strncmp(p2, "/lib", 4)
1461            && strncmp(p2, "/sbin", 4) && strncmp(p2, "/usr", 4))
1462     // absolute path, but not a system command
1463     need_ginge = 1;
1464
1465   p2 = emu_wrap_path(command);
1466   if (need_ginge) {
1467     make_local_path(tmp_path, sizeof(tmp_path), "ginge_prep");
1468     p = tmp_path + strlen(tmp_path);
1469
1470     snprintf(p, sizeof(tmp_path) - (p - tmp_path), " --nomenu %s", p2);
1471   }
1472   else
1473     snprintf(tmp_path, sizeof(tmp_path), "%s", p2);
1474   emu_wrap_path_free(p2, command);
1475
1476   dbg("system: \"%s\"\n", tmp_path);
1477
1478   // the app might want the screen too..
1479   fb_thread_pause();
1480   ret = system(tmp_path);
1481   fb_thread_resume();
1482   return ret;
1483 }
1484
1485 long emu_do_execve(const char *filename, char * const argv[],
1486                    char * const envp[])
1487 {
1488   const char **new_argv;
1489   char *prep_path;
1490   int i, argc;
1491   long ret;
1492
1493   if (filename == NULL)
1494     return -1;
1495
1496   if (strstr(filename, "gp2xmenu") != NULL)
1497     host_forced_exit(0);
1498
1499   for (i = 0; argv[i] != NULL; i++)
1500     ;
1501   argc = i + 1;
1502
1503   new_argv = calloc(argc + 2, sizeof(new_argv[0]));
1504   if (new_argv == NULL)
1505     return -1;
1506
1507   prep_path = malloc(512);
1508   if (prep_path == NULL)
1509     return -1;
1510
1511   make_local_path(prep_path, 512, "ginge_prep");
1512   new_argv[0] = prep_path;
1513   new_argv[1] = "--nomenu";
1514   new_argv[2] = emu_wrap_path(filename);
1515
1516   if (argv[0] != NULL)
1517     for (i = 1; argv[i] != NULL; i++)
1518       new_argv[i + 2] = argv[i];
1519
1520   dbg("execve \"%s\" %s \"%s\"\n", new_argv[0], new_argv[1], new_argv[2]);
1521   ret = execve(new_argv[0], (char **)new_argv, envp);
1522   err("execve(%s): %ld\n", new_argv[0], ret);
1523   return ret;
1524 }
1525
1526 // vim:shiftwidth=2:expandtab