Browse Source

sms, basic gamegear support

root 2 years ago
parent
commit
2b919ad68c

+ 57 - 49
pico/draw.c

@@ -135,7 +135,7 @@ void blockcpy_or(void *dst, void *src, size_t n, int pat)
   for (; n; n--)
     *pd++ = (unsigned char) (*ps++ | pat);
 }
-#define blockcpy memcpy
+#define blockcpy memmove
 #endif
 
 #define TileNormMaker_(pix_func,ret)                         \
@@ -1642,21 +1642,28 @@ void FinalizeLine555(int sh, int line, struct PicoEState *est)
 
   PicoDrawUpdateHighPal();
 
-  if (Pico.video.reg[12]&1) {
-    len = 320;
-  } else {
-    len = 256;
-  }
-
-  if ((*est->PicoOpt & POPT_EN_SOFTSCALE) && len == 256) {
-    switch (PicoIn.filter) {
-    case 3: h_upscale_bl4_4_5(pd, 320, ps, 256, 256, f_pal); break;
-    case 2: h_upscale_bl2_4_5(pd, 320, ps, 256, 256, f_pal); break;
-    case 1: h_upscale_snn_4_5(pd, 320, ps, 256, 256, f_pal); break;
-    default: h_upscale_nn_4_5(pd, 320, ps, 256, 256, f_pal); break;
-    }
+  if ((PicoIn.AHW & PAHW_SMS) && (Pico.m.hardware & 0x3) == 0x3)
+                                        len = 160;
+  else if (Pico.video.reg[12]&1)        len = 320;
+  else                                  len = 256;
+
+  if ((*est->PicoOpt & POPT_EN_SOFTSCALE) && len < 320) {
+    if (len == 256)
+      switch (PicoIn.filter) {
+      case 3: h_upscale_bl4_4_5(pd, 320, ps, 256, len, f_pal); break;
+      case 2: h_upscale_bl2_4_5(pd, 320, ps, 256, len, f_pal); break;
+      case 1: h_upscale_snn_4_5(pd, 320, ps, 256, len, f_pal); break;
+      default: h_upscale_nn_4_5(pd, 320, ps, 256, len, f_pal); break;
+      }
+    else if (len == 160)
+      switch (PicoIn.filter) {
+      case 3:
+      case 2: h_upscale_bl2_1_2(pd, 320, ps, 160, len, f_pal); break;
+      default: h_upscale_nn_1_2(pd, 320, ps, 160, len, f_pal); break;
+      }
   } else {
-    if (!(*est->PicoOpt & POPT_DIS_32C_BORDER) && len == 256) pd += 32;
+    if (!(*est->PicoOpt & POPT_DIS_32C_BORDER) && len < 320)
+      pd += (320-len) / 2;
 #if 1
     h_copy(pd, 320, ps, 320, len, f_pal);
 #else
@@ -1670,9 +1677,10 @@ void FinalizeLine555(int sh, int line, struct PicoEState *est)
 }
 #endif
 
-static void FinalizeLine8bit(int sh, int line, struct PicoEState *est)
+void FinalizeLine8bit(int sh, int line, struct PicoEState *est)
 {
   unsigned char *pd = est->DrawLineDest;
+  unsigned char *ps = est->HighCol+8;
   int len;
   static int dirty_line;
 
@@ -1680,8 +1688,8 @@ static void FinalizeLine8bit(int sh, int line, struct PicoEState *est)
   {
     // a hack for mid-frame palette changes
     if (!(est->rendstatus & PDRAW_SONIC_MODE) | (line - dirty_line > 4)) {
-      // store a maximum of 2 additional palettes in SonicPal
-      if (est->SonicPalCount < 2)
+      // store a maximum of 3 additional palettes in SonicPal
+      if (est->SonicPalCount < 3)
         est->SonicPalCount ++;
       dirty_line = line;
       est->rendstatus |= PDRAW_SONIC_MODE;
@@ -1690,35 +1698,33 @@ static void FinalizeLine8bit(int sh, int line, struct PicoEState *est)
     Pico.m.dirtyPal = 2;
   }
 
-  if (Pico.video.reg[12]&1) {
-    len = 320;
-  } else {
-    len = 256;
-  }
+  if ((PicoIn.AHW & PAHW_SMS) && (Pico.m.hardware & 0x3) == 0x3)
+                                        len = 160;
+  else if (Pico.video.reg[12]&1)        len = 320;
+  else                                  len = 256;
+
+  if (DrawLineDestIncrement == 0)
+    pd = est->HighCol+8;
 
-  if ((PicoIn.opt & POPT_EN_SOFTSCALE) && len == 256) {
-    unsigned char *ps = est->HighCol+8;
+  if ((PicoIn.opt & POPT_EN_SOFTSCALE) && len < 320) {
     unsigned char pal = 0;
 
     if (!sh && (est->rendstatus & PDRAW_SONIC_MODE))
       pal = est->SonicPalCount*0x40;
-    if (DrawLineDestIncrement == 0)
-      pd = est->HighCol+8;
     // Smoothing can't be used with CLUT, hence it's always Nearest Neighbour.
-    // use reverse version since src and dest ptr may be the same.
-    rh_upscale_nn_4_5(pd, 320, ps, 256, len, f_or);
-  } else if (DrawLineDestIncrement == 0) {
-    if (!sh && (est->rendstatus & PDRAW_SONIC_MODE))
-      blockcpy_or(est->HighCol+8, est->HighCol+8, len, est->SonicPalCount*0x40);
+    if (len == 256)
+      // use reverse version since src and dest ptr may be the same.
+      rh_upscale_nn_4_5(pd, 320, ps, 256, len, f_or);
+    else
+      rh_upscale_nn_1_2(pd, 320, ps, 256, len, f_or);
   } else {
-    if (!(PicoIn.opt & POPT_DIS_32C_BORDER))
-      pd += 32;
-    if (!sh && (est->rendstatus & PDRAW_SONIC_MODE)) {
+    if (!(*est->PicoOpt & POPT_DIS_32C_BORDER) && len < 320)
+      pd += (320-len) / 2;
+    if (!sh && (est->rendstatus & PDRAW_SONIC_MODE))
       // select active backup palette
-      blockcpy_or(pd, est->HighCol+8, len, est->SonicPalCount*0x40);
-    } else {
-      blockcpy(pd, est->HighCol+8, len);
-    }
+      blockcpy_or(pd, ps, len, est->SonicPalCount*0x40);
+    else if (pd != ps)
+      blockcpy(pd, ps, len);
   }
 }
 
@@ -1893,8 +1899,15 @@ PICO_INTERNAL void PicoFrameStart(void)
 
 static void DrawBlankedLine(int line, int offs, int sh, int bgc)
 {
-  if (PicoScanBegin != NULL)
-    PicoScanBegin(line + offs);
+  int skip = skip_next_line;
+
+  if (PicoScanBegin != NULL && skip == 0)
+    skip = PicoScanBegin(line + offs);
+
+  if (skip) {
+    skip_next_line = skip - 1;
+    return;
+  }
 
   BackFill(bgc, sh, &Pico.est);
 
@@ -1902,7 +1915,7 @@ static void DrawBlankedLine(int line, int offs, int sh, int bgc)
     FinalizeLine(sh, line, &Pico.est);
 
   if (PicoScanEnd != NULL)
-    PicoScanEnd(line + offs);
+    skip_next_line = PicoScanEnd(line + offs);
 
   Pico.est.HighCol += HighColIncrement;
   Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;
@@ -1910,15 +1923,10 @@ static void DrawBlankedLine(int line, int offs, int sh, int bgc)
 
 static void PicoLine(int line, int offs, int sh, int bgc)
 {
-  int skip = 0;
-
-  if (skip_next_line > 0) {
-    skip_next_line--;
-    return;
-  }
+  int skip = skip_next_line;
 
   Pico.est.DrawScanline = line;
-  if (PicoScanBegin != NULL)
+  if (PicoScanBegin != NULL && skip == 0)
     skip = PicoScanBegin(line + offs);
 
   if (skip) {

+ 171 - 16
pico/draw_arm.S

@@ -1642,27 +1642,42 @@ FinalizeLine555:
     add     r3, r10, #OFS_EST_HighPal
 
     mov     lr, #0xff
+    mov     lr, lr, lsl #1
 
+    ldr     r5, [r10, #OFS_EST_PicoOpt]
     ldr     r1, [r10, #OFS_EST_HighCol]
     ldr     r0, [r10, #OFS_EST_DrawLineDest]
+    ldr     r4, [r5]
+    ldr     r7, [r5, #OFS_PicoIn_AHW-OFS_PicoIn_opt]
+    ldrb    r12,[r8, #OFS_Pico_video_reg+12]
+    ldr     r2, [r8, #OFS_Pico_m_hardware]
     add     r1, r1, #8
 
-    ldrb    r12, [r8, #OFS_Pico_video_reg+12]
-    mov     lr, lr, lsl #1
+    tst     r7, #0x10
+    beq     .fl_no20colRGB555
+    and     r7, r2, #0x3
+    cmp     r7, #0x3		@ Game Gear, LCD?
+    bne     .fl_no20colRGB555
 
-    tst     r12, #1
-    movne   r2, #320/8           @ len
+    mov     r2, #160/8		@ len = 160
+    tst     r4, #0x4000		@ EN_SOFTSCALE?
+    bne     .fl_20scale_RGB555	@ scale 160->320
+    beq     .fl_checkborder
+
+.fl_no20colRGB555:
+    tst     r12, #1		@ h32?
+    movne   r2, #320/8          @ len = 320
     bne     .fl_no32colRGB555
-    ldr     r5, [r10, #OFS_EST_PicoOpt]
-    mov     r2, #256/8
-    ldr     r4, [r5]
-    tst     r4, #0x4000
-    bne     .fl_32scale_RGB555
-    tst     r4, #0x0100
-    addeq   r0, r0, #32*2
+    moveq   r2, #256/8		@ len = 256
+    tst     r4, #0x4000		@ EN_SOFTSCALE?
+    bne     .fl_32scale_RGB555	@ scale 256->320
 
-.fl_no32colRGB555:
+.fl_checkborder:
+    tst     r4, #0x0100		@ DIS_32C_BORDER?
+    rsbeq   r4, r2, #320/8	@ pd += (320-len)/2
+    addeq   r0, r0, r4, lsl #3
 
+.fl_no32colRGB555:
 #ifdef UNALIGNED_DRAWLINEDEST
     @ this is basically for Gizmondo, which has unaligned odd lines in the framebuffer
     tst     r0, #2
@@ -1715,9 +1730,9 @@ FinalizeLine555:
     bne     .fl_32scale_RGB555u
 #endif
 
-    ands    r5, r5, #0x3
-    addne   pc, pc, r5, lsl #2
-    b       .fl_32scale_nn
+    and     r5, r5, #0x3
+    add     pc, pc, r5, lsl #2
+    nop
     b       .fl_32scale_nn
     b       .fl_32scale_snn
     b       .fl_32scale_bl2
@@ -1866,6 +1881,9 @@ FinalizeLine555:
     ldmfd   sp!, {r4-r10,pc}
 
 .fl_32scale_bl4:
+    // TODO this should reflect the bl4 C algorithm, but it doesn't, it's bln.
+    and     r9, r9, r9, lsl #1        @ nuke 2 LSBs to avoid spilling for n/4
+.fl_32loop_bl4:
     ldr     r12, [r1], #4
     ldr     r7,  [r1], #4
 
@@ -1926,10 +1944,147 @@ FinalizeLine555:
     subs    r2, r2, #1
 
     stmia   r0!, {r4,r5,r6,r8,r10}
-    bne     .fl_32scale_bl4
+    bne     .fl_32loop_bl4
+
+    ldmfd   sp!, {r4-r10,pc}
+
+.fl_20scale_RGB555:
+    ldr     r5, [r5, #OFS_PicoIn_filter-OFS_PicoIn_opt]
+
+    mov     r9, #0xf700 @ f800 07e0 001f | e000 0780 001c | 3800 01e0 0007
+    orr     r9, r9, #0x00de
+
+#ifdef UNALIGNED_DRAWLINEDEST
+    tst     r0, #2
+    bne     .fl_20scale_RGB555u
+#endif
+
+    and     r5, r5, #0x2
+    add     pc, pc, r5, lsl #1
+    nop
+    b       .fl_20scale_nn
+    b       .fl_20scale_bl2
+
+.fl_20scale_nn:
+    ldr     r12, [r1], #4
+    ldr     r7,  [r1], #4
+
+    and     r4, lr, r12, lsl #1
+    ldrh    r4, [r3, r4]
+    and     r5, lr, r12, lsr #7
+    ldrh    r5, [r3, r5]
+    and     r6, lr, r12, lsr #15
+    ldrh    r6, [r3, r6]
+    and     r8 ,lr, r12, lsr #23
+    ldrh    r8 ,[r3, r8 ]
+
+    orr     r4, r4, r4, lsl #16
+    orr     r5, r5, r5, lsl #16
+    orr     r6, r6, r6, lsl #16
+    orr     r8, r8, r8, lsl #16
+    stmia   r0!, {r4,r5,r6,r8}
+
+    and     r4, lr, r7, lsl #1
+    ldrh    r4, [r3, r4]
+    and     r5, lr, r7, lsr #7
+    ldrh    r5, [r3, r5]
+    and     r6 ,lr, r7, lsr #15
+    ldrh    r6 ,[r3, r6]
+    and     r8, lr, r7, lsr #23
+    ldrh    r8, [r3, r8]
+
+    orr     r4, r4, r4, lsl #16
+    orr     r5, r5, r5, lsl #16
+    orr     r6, r6, r6, lsl #16
+    orr     r8, r8, r8, lsl #16
+    stmia   r0!, {r4,r5,r6,r8}
+
+    subs    r2, r2, #1
+    bne     .fl_20scale_nn
+
+    ldmfd   sp!, {r4-r10,pc}
+
+
+.fl_20scale_bl2:
+    ldr     r8,  [r1]
+    and     r8, lr, r8, lsl #1
+    ldrh    r8, [r3, r8]
+    and     r8, r8, r9
+    lsl     r8, r8, #16
+
+.fl_20loop_bl2:
+    ldr     r12, [r1], #4
+    ldr     r7,  [r1], #4
+
+    and     r4, lr, r12, lsl #1
+    ldrh    r4, [r3, r4]
+    and     r5, lr, r12, lsr #7
+    ldrh    r5, [r3, r5]
+    and     r6, lr, r12, lsr #15
+    ldrh    r6, [r3, r6]
+
+    and     r4, r4, r9
+    add     r10,r4, r8, lsr #16
+    mov     r10,r10,lsr #1
+    orr     r4, r10,r4, lsl #16		@ (px-1+px0)/2 | px0
+
+    and     r8 ,lr, r12, lsr #23
+    ldrh    r8 ,[r3, r8]
+
+    and     r5, r5, r9
+    add     r10,r5, r4, lsr #16
+    mov     r10,r10,lsr #1
+    orr     r5, r10,r5, lsl #16		@ (px0 +px1)/2 | px1
+
+    and     r6, r6, r9
+    add     r10,r6, r5, lsr #16
+    mov     r10,r10,lsr #1
+    orr     r6, r10,r6, lsl #16		@ (px1 +px2)/2 | px2
+
+    and     r8, r8, r9
+    add     r10,r8, r6, lsr #16
+    mov     r10,r10,lsr #1
+    orr     r8, r10,r8, lsl #16		@ (px2 +px3)/2 | px3
+
+    stmia   r0!, {r4,r5,r6,r8}
+
+    and     r4, lr, r7, lsl #1
+    ldrh    r4, [r3, r4]
+    and     r5, lr, r7, lsr #7
+    ldrh    r5, [r3, r5]
+    and     r6, lr, r7, lsr #15
+    ldrh    r6, [r3, r6]
+
+    and     r4, r4, r9
+    add     r10,r4, r8, lsr #16
+    mov     r10,r10,lsr #1
+    orr     r4, r10,r4, lsl #16		@ (px-1+px0)/2 | px0
+
+    and     r8 ,lr, r7, lsr #23
+    ldrh    r8 ,[r3, r8]
+
+    and     r5, r5, r9
+    add     r10,r5, r4, lsr #16
+    mov     r10,r10,lsr #1
+    orr     r5, r10,r5, lsl #16		@ (px0 +px1)/2 | px1
+
+    and     r6, r6, r9
+    add     r10,r6, r5, lsr #16
+    mov     r10,r10,lsr #1
+    orr     r6, r10,r6, lsl #16		@ (px1 +px2)/2 | px2
+
+    and     r8, r8, r9
+    add     r10,r8, r6, lsr #16
+    mov     r10,r10,lsr #1
+    orr     r8, r10,r8, lsl #16		@ (px2 +px3)/2 | px3
+
+    subs    r2, r2, #1
+    stmia   r0!, {r4,r5,r6,r8}
+    bne     .fl_20loop_bl2
 
     ldmfd   sp!, {r4-r10,pc}
 
+
 #ifdef UNALIGNED_DRAWLINEDEST
     @ unaligned versions of loops
     @ warning: starts drawing 2bytes before dst

+ 10 - 2
pico/media.c

@@ -248,7 +248,6 @@ enum media_type_e PicoLoadMedia(const char *filename,
     }
   }
   else if (media_type == PM_MARK3) {
-    lprintf("detected SMS ROM\n");
     PicoIn.AHW = PAHW_SMS;
   }
 
@@ -297,9 +296,18 @@ enum media_type_e PicoLoadMedia(const char *filename,
     goto out;
   }
   rom_data = NULL; // now belongs to PicoCart
-  Pico.m.ncart_in = 0;
+
+  // simple test for GG. Do this here since m.hardware is nulled in Insert
+  if (PicoIn.AHW & PAHW_SMS) {
+    if (strstr(filename,".gg")) {
+      Pico.m.hardware |= 0x1;
+      lprintf("detected GG ROM\n");
+    } else
+      lprintf("detected SMS ROM\n");
+  }
 
   // insert CD if it was detected
+  Pico.m.ncart_in = 0;
   if (cd_img_type != CT_UNKNOWN) {
     ret = cdd_load(filename, cd_img_type);
     if (ret != 0) {

+ 63 - 54
pico/mode4.c

@@ -141,6 +141,8 @@ static void DrawSpritesM4(int scanline)
   if (pv->reg[0] & 8)
     xoff = 0;
   xoff += line_offset;
+  if ((Pico.m.hardware & 0x3) == 0x3)
+    xoff -= 48; // GG LCD, adjust to center 160 px
 
   sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7);
   if (pv->reg[1] & 2) {
@@ -164,10 +166,12 @@ static void DrawSpritesM4(int scanline)
       break;
     }
 
-    sprites_x[s] = xoff + sat[MEM_LE2(0x80 + i*2)];
-    sprites_addr[s] = sprite_base + ((sat[MEM_LE2(0x80 + i*2 + 1)] & addr_mask) << (5-1)) +
-      ((scanline - y) >> zoomed << (2-1));
-    s++;
+    if (xoff + sat[MEM_LE2(0x80 + i*2)] >= 0) {
+      sprites_x[s] = xoff + sat[MEM_LE2(0x80 + i*2)];
+      sprites_addr[s] = sprite_base + ((sat[MEM_LE2(0x80 + i*2 + 1)] & addr_mask) << (5-1)) +
+        ((scanline - y) >> zoomed << (2-1));
+      s++;
+    }
   }
 
   // really half-assed but better than nothing
@@ -296,9 +300,12 @@ static void DrawDisplayM4(int scanline)
 
   // low priority tiles
   if (!(pv->debug_p & PVD_KILL_B)) {
-    if (pv->reg[0] & 0x80) {
+    if ((Pico.m.hardware & 0x3) == 0x3) {
+      // on GG render only the center 160 px
+      DrawStripLowM4(nametab , dx | ((cells-12)<< 16),(tilex+6) | (ty  << 16));
+    } else if (pv->reg[0] & 0x80) {
       // vscroll disabled for rightmost 8 columns (e.g. Gauntlet)
-      int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline & 7;
+      int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline&7;
       DrawStripLowM4(nametab,  dx | ((cells-8) << 16), tilex  | (ty  << 16));
       DrawStripLowM4(nametab2, dx2 |       (8  << 16), tilex2 | (ty2 << 17));
     } else
@@ -311,8 +318,10 @@ static void DrawDisplayM4(int scanline)
 
   // high priority tiles (use virtual layer switch just for fun)
   if (!(pv->debug_p & PVD_KILL_A)) {
-    if (pv->reg[0] & 0x80) {
-      int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline & 7;
+    if ((Pico.m.hardware & 0x3) == 0x3) {
+      DrawStripHighM4(nametab , dx | ((cells-12)<< 16),(tilex+6) | (ty  << 16));
+    } else if (pv->reg[0] & 0x80) {
+      int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline&7;
       DrawStripHighM4(nametab,  dx | ((cells-8) << 16), tilex  | (ty  << 16));
       DrawStripHighM4(nametab2, dx2 |       (8  << 16), tilex2 | (ty2 << 17));
     } else
@@ -451,20 +460,21 @@ static void DrawSpritesM2(int scanline)
 
   // now draw all sprites backwards
   for (--s; s >= 0; s--) {
-    int x, w = (zoomed ? 16: 8);
+    int x, c, w = (zoomed ? 16: 8);
     i = sprites_x[s];
     x = sat[MEM_LE2(i+1)] + xoff;
     if (sat[MEM_LE2(i+3)] & 0x80)
       x -= 32;
+    c = sat[MEM_LE2(i+3)] & 0x0f;
     if (x > 0) {
       pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])];
-      if (zoomed) TileDoubleSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf);
-      else        TileNormSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf);
+      if (zoomed) TileDoubleSprM2(x, pack, c);
+      else        TileNormSprM2(x, pack, c);
     }
     if((pv->reg[1] & 0x2) && (x+=w) > 0) {
       pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)];
-      if (zoomed) TileDoubleSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf);
-      else        TileNormSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf);
+      if (zoomed) TileDoubleSprM2(x, pack, c);
+      else        TileNormSprM2(x, pack, c);
     }
   }
 }
@@ -524,34 +534,45 @@ static void FinalizeLineRGB555SMS(int line);
 
 void PicoFrameStartSMS(void)
 {
-  int lines = 192, columns = 256, coffs;
+  int lines = 192, columns = 256, loffs, coffs;
   skip_next_line = 0;
-  screen_offset = 24;
+  loffs = screen_offset = 24; // 192 lines is really 224 with top/bottom bars
   Pico.est.rendstatus = PDRAW_32_COLS;
 
-  switch ((Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18)) {
+  // Copy LCD enable flag for easier handling
+  Pico.m.hardware &= ~0x2;
+  if (PicoIn.opt & POPT_EN_GG_LCD)
+    Pico.m.hardware |= 0x2;
+
+  if ((Pico.m.hardware & 0x3) == 0x3) {
+    // GG LCD always has 160x144 regardless of settings
+    screen_offset = 24; // nonetheless the vdp timing has 224 lines
+    loffs = 48;
+    lines = 144;
+    columns = 160;
+  } else switch ((Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18)) {
   // SMS2 only 224/240 line modes, e.g. Micro Machines
   case 0x06|0x08:
-      screen_offset = 0;
+      loffs = screen_offset = 0;
       lines = 240;
       break;
   case 0x06|0x10:
-      screen_offset = 8;
+      loffs = screen_offset = 8;
       lines = 224;
       break;
   }
   if (PicoIn.opt & POPT_EN_SOFTSCALE) {
-    line_offset = 0;
+    coffs = 0;
     columns = 320;
   } else
-    line_offset = PicoIn.opt & POPT_DIS_32C_BORDER ? 0 : 32;
+    coffs = PicoIn.opt & POPT_DIS_32C_BORDER ? 0:(320-columns)/2;
+  line_offset = (PicoIn.opt & POPT_ALT_RENDERER ? coffs : 0);
 
-  coffs = line_offset;
   if (FinalizeLineSMS == FinalizeLineRGB555SMS)
     line_offset = 0 /* done in FinalizeLine */;
 
   if (Pico.est.rendstatus != rendstatus_old || lines != rendlines) {
-    emu_video_mode_change(screen_offset, lines, coffs, columns);
+    emu_video_mode_change(loffs, lines, coffs, columns);
     rendstatus_old = Pico.est.rendstatus;
     rendlines = lines;
   }
@@ -562,14 +583,20 @@ void PicoFrameStartSMS(void)
 
 void PicoLineSMS(int line)
 {
-  if (skip_next_line > 0) {
-    skip_next_line--;
+  int skip = skip_next_line;
+
+  // GG LCD, render only visible part of screen
+  if ((Pico.m.hardware & 0x3) == 0x3 && (line < 24 || line >= 24+144))
+    goto norender;
+
+  if (PicoScanBegin != NULL && skip == 0)
+    skip = PicoScanBegin(line + screen_offset);
+
+  if (skip) {
+    skip_next_line = skip - 1;
     return;
   }
 
-  if (PicoScanBegin != NULL)
-    skip_next_line = PicoScanBegin(line + screen_offset);
-
   // Draw screen:
   BackFill(Pico.video.reg[7] & 0x0f, 0, &Pico.est);
   if (Pico.video.reg[1] & 0x40) {
@@ -583,19 +610,21 @@ void PicoLineSMS(int line)
   if (PicoScanEnd != NULL)
     skip_next_line = PicoScanEnd(line + screen_offset);
 
+norender:
   Pico.est.HighCol += HighColIncrement;
   Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;
 }
 
 /* Fixed palette for TMS9918 modes */
 static u16 tmspal[32] = {
- 0x00,0x00,0x08,0x0c,0x10,0x30,0x01,0x3c,0x02,0x03,0x05,0x0f,0x04,0x33,0x15,0x3f
+  0x0000, 0x0000, 0x00a0, 0x00f0, 0x0500, 0x0f00, 0x0005, 0x0ff0,
+  0x000a, 0x000f, 0x0055, 0x00ff, 0x0050, 0x0f0f, 0x0555, 0x0fff,
 };
 
 void PicoDoHighPal555SMS(void)
 {
-  unsigned int *spal=(void *)PicoMem.cram;
-  unsigned int *dpal=(void *)Pico.est.HighPal;
+  u32 *spal=(void *)PicoMem.cram;
+  u32 *dpal=(void *)Pico.est.HighPal;
   unsigned int t;
   int i;
 
@@ -603,21 +632,9 @@ void PicoDoHighPal555SMS(void)
   if (!(Pico.video.reg[0] & 0x4))
     spal = (u32 *)tmspal;
 
-  /* cram is always stored as shorts, even though real hardware probably uses bytes */
-  if (PicoIn.AHW & PAHW_SMS) for (i = 0x20/2; i > 0; i--, spal++, dpal++) { 
-    t = *spal;
-#if defined(USE_BGR555)
-    t = ((t & 0x00030003)<< 3) | ((t & 0x000c000c)<<6) | ((t & 0x00300030)<<9);
-    t |= (t >> 2) | ((t >> 4) & 0x04210421);
-#elif defined(USE_BGR565)
-    t = ((t & 0x00030003)<< 3) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)<<10);
-    t |= (t >> 2) | ((t >> 4) & 0x08610861);
-#else
-    t = ((t & 0x00030003)<<14) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)>>1);
-    t |= (t >> 2) | ((t >> 4) & 0x08610861);
-#endif
-    *dpal = t;
-  } else for (i = 0x20/2; i > 0; i--, spal++, dpal++) { // GG palette 4 bit/col
+  /* SMS 6 bit cram data was already converted to MD/GG format by vdp write,
+   * hence GG/SMS/TMS can all be handled the same here */
+  for (i = 0x20/2; i > 0; i--, spal++, dpal++) { 
     t = *spal;
 #if defined(USE_BGR555)
     t = ((t & 0x000f000f)<< 1) | ((t & 0x00f000f0)<<2) | ((t & 0x0f000f00)<<3);
@@ -646,15 +663,7 @@ static void FinalizeLineRGB555SMS(int line)
 
 static void FinalizeLine8bitSMS(int line)
 {
-  u8 *pd = Pico.est.DrawLineDest + line_offset;
-  u8 *ps = Pico.est.HighCol + line_offset + 8;
-
-  if (DrawLineDestIncrement) {
-    if (PicoIn.opt & POPT_EN_SOFTSCALE)
-      rh_upscale_nn_4_5(pd, 320, ps, 256, 256, f_nop);
-    else if (pd != ps)
-      memcpy(pd, ps, 256);
-  }
+  FinalizeLine8bit(0, line, &Pico.est);
 }
 
 void PicoDrawSetOutputSMS(pdso_t which)

+ 1 - 1
pico/pico.h

@@ -64,7 +64,7 @@ extern void *p32x_bios_g, *p32x_bios_m, *p32x_bios_s;
 #define POPT_EN_MCD_PCM     (1<<10)
 #define POPT_EN_MCD_CDDA    (1<<11)
 #define POPT_EN_MCD_GFX     (1<<12) // 00 x000
-// unused                   (1<<13)
+#define POPT_EN_GG_LCD      (1<<13)
 #define POPT_EN_SOFTSCALE   (1<<14)
 #define POPT_EN_MCD_RAMCART (1<<15)
 #define POPT_DIS_VDP_FIFO   (1<<16) // 0x 0000

+ 1 - 0
pico/pico_int.h

@@ -670,6 +670,7 @@ PICO_INTERNAL void PicoFrameStart(void);
 void PicoDrawSync(int to, int blank_last_line);
 void BackFill(int reg7, int sh, struct PicoEState *est);
 void FinalizeLine555(int sh, int line, struct PicoEState *est);
+void FinalizeLine8bit(int sh, int line, struct PicoEState *est);
 void PicoDrawSetOutBufMD(void *dest, int increment);
 extern int (*PicoScanBegin)(unsigned int num);
 extern int (*PicoScanEnd)(unsigned int num);

+ 12 - 3
pico/sms.c

@@ -52,8 +52,17 @@ static void vdp_data_write(unsigned char d)
   struct PicoVideo *pv = &Pico.video;
 
   if (pv->type == 3) {
-    if (PicoMem.cram[pv->addr & 0x1f] != d) Pico.m.dirtyPal = 1;
-    PicoMem.cram[pv->addr & 0x1f] = d;
+    if (Pico.m.hardware & 0x1) { // GG, same layout as MD
+      unsigned a = pv->addr & 0x3f;
+      if (a & 0x1) d &= 0x0f;
+      if (((u8 *)PicoMem.cram)[MEM_LE2(a)] != d) Pico.m.dirtyPal = 1;
+      ((u8 *)PicoMem.cram)[MEM_LE2(a)] = d;
+    } else { // SMS, convert to MD layout (00BbGgRr to 0000BbBbGgGgRrRr)
+      unsigned a = pv->addr & 0x1f;
+      u16 c = (d&0x30)*0x40 + (d&0x0c)*0x10 + (d&0x03)*0x04;
+      if (PicoMem.cram[a] != (c | (c>>2))) Pico.m.dirtyPal = 1;
+      PicoMem.cram[a] = c | (c>>2);
+    }
   } else {
     PicoMem.vramb[MEM_LE2(pv->addr)] = d;
   }
@@ -130,7 +139,7 @@ static unsigned char z80_sms_in(unsigned short a)
     {
       case 0x00:
       case 0x01:
-        d = 0xff;
+        d = 0xff & ~(PicoIn.pad[0] & 0x80);
         break;
 
       case 0x40: /* V counter */

+ 8 - 11
platform/common/arm_utils.s

@@ -58,9 +58,6 @@
 .endif
 
     orr     r2,  r2,   r2,  lsr #3
-.if \sh == 1
-    str     r2, [r0, #0x40*2*4]
-.endif
     str     r2, [r0], #4
 .endm
 
@@ -101,10 +98,10 @@ bgr444_to_rgb32_sh:
     subs    r12, r12, #1
 
     ldmia    r1!, {r4-r7}
-    convRGB32_2 r4, 1
-    convRGB32_2 r5, 1
-    convRGB32_2 r6, 1
-    convRGB32_2 r7, 1
+    convRGB32_2 r4, 2
+    convRGB32_2 r5, 2
+    convRGB32_2 r6, 2
+    convRGB32_2 r7, 2
     bgt     .loopRGB32sh
 
     mov     r12, #0x40>>3 @ repeats
@@ -112,10 +109,10 @@ bgr444_to_rgb32_sh:
 
 .loopRGB32hi:
      ldmia    r1!, {r4-r7}
-    convRGB32_2 r4, 2
-    convRGB32_2 r5, 2
-    convRGB32_2 r6, 2
-    convRGB32_2 r7, 2
+    convRGB32_2 r4, 1
+    convRGB32_2 r5, 1
+    convRGB32_2 r6, 1
+    convRGB32_2 r7, 1
 
     subs    r12, r12, #1
     bgt     .loopRGB32hi

+ 1925 - 1923
platform/common/emu.c

@@ -1,1923 +1,1925 @@
-/*
- * PicoDrive
- * (C) notaz, 2007-2010
- *
- * This work is licensed under the terms of MAME license.
- * See COPYING file in the top-level directory.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#ifdef __GP2X__
-#include <unistd.h>
-#endif
-
-#include "../libpicofe/posix.h"
-#include "../libpicofe/input.h"
-#include "../libpicofe/fonts.h"
-#include "../libpicofe/sndout.h"
-#include "../libpicofe/lprintf.h"
-#include "../libpicofe/plat.h"
-#include "emu.h"
-#include "configfile_fk.h"
-#include "input_pico.h"
-#include "menu_pico.h"
-#include "config_file.h"
-
-#include <pico/pico_int.h>
-#include <pico/patch.h>
-
-#ifdef USE_LIBRETRO_VFS
-#include "file_stream_transforms.h"
-#endif
-
-#if defined(__GNUC__) && __GNUC__ >= 7
-#pragma GCC diagnostic ignored "-Wformat-truncation"
-#endif
-
-#ifndef _WIN32
-#define PATH_SEP      "/"
-#define PATH_SEP_C    '/'
-#else
-#define PATH_SEP      "\\"
-#define PATH_SEP_C    '\\'
-#endif
-
-#define STATUS_MSG_TIMEOUT 2000
-
-void *g_screen_ptr;
-
-int g_screen_width  = 320;
-int g_screen_height = 240;
-int g_screen_ppitch = 320; // pitch in pixels
-
-const char *PicoConfigFile = "config2.cfg";
-currentConfig_t currentConfig, defaultConfig;
-int state_slot = 0;
-int config_slot = 0, config_slot_current = 0;
-int pico_pen_x = 320/2, pico_pen_y = 240/2;
-int pico_inp_mode;
-int flip_after_sync;
-int engineState = PGS_Menu;
-int show_fps_bypass = 0;
-int need_screen_cleared = 0;
-
-static short __attribute__((aligned(4))) sndBuffer[2*44100/50];
-
-/* tmp buff to reduce stack usage for plats with small stack */
-static char static_buff[1024];
-const char *rom_fname_reload;
-char rom_fname_loaded[1024];
-int reset_timing = 0;
-static unsigned int notice_msg_time;	/* when started showing */
-static char noticeMsg[40];
-
-unsigned char *movie_data = NULL;
-static int movie_size = 0;
-
-
-/* don't use tolower() for easy old glibc binary compatibility */
-static void strlwr_(char *string)
-{
-	char *p;
-	for (p = string; *p; p++)
-		if ('A' <= *p && *p <= 'Z')
-			*p += 'a' - 'A';
-}
-
-static int try_rfn_cut(char *fname)
-{
-	FILE *tmp;
-	char *p;
-
-	p = fname + strlen(fname) - 1;
-	for (; p > fname; p--)
-		if (*p == '.') break;
-	*p = 0;
-
-	if((tmp = fopen(fname, "rb"))) {
-		fclose(tmp);
-		return 1;
-	}
-	return 0;
-}
-
-static void get_ext(const char *file, char *ext)
-{
-	const char *p;
-
-	p = file + strlen(file) - 4;
-	if (p < file) p = file;
-	strncpy(ext, p, 4);
-	ext[4] = 0;
-	strlwr_(ext);
-}
-
-static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext, const char *fname)
-{
-	int prefix_len = 0;
-	const char *p;
-
-	*dst = 0;
-	if (prefix) {
-		/*int len = plat_get_root_dir(dst, dstlen);
-		strcpy(dst + len, prefix);
-		prefix_len = len + strlen(prefix);*/
-
-		/* Saves are in ROM folder */
-		prefix_len = strlen(mRomPath)+1;
-		sprintf(dst, "%s/", mRomPath);
-	}
-
-	p = fname + strlen(fname) - 1;
-	for (; p >= fname && *p != PATH_SEP_C; p--)
-		;
-	p++;
-	strncpy(dst + prefix_len, p, dstlen - prefix_len - 1);
-
-	dst[dstlen - 8] = 0;
-	if ((p = strrchr(dst, '.')) != NULL)
-		dst[p-dst] = 0;
-	if (ext)
-		strcat(dst, ext);
-}
-
-static void romfname_ext(char *dst, int dstlen, const char *prefix, const char *ext)
-{
-	fname_ext(dst, dstlen, prefix, ext, rom_fname_loaded);
-}
-
-void emu_status_msg(const char *format, ...)
-{
-	va_list vl;
-	int ret;
-
-	va_start(vl, format);
-	ret = vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);
-	va_end(vl);
-
-	/* be sure old text gets overwritten */
-	for (; ret < 28; ret++)
-		noticeMsg[ret] = ' ';
-	noticeMsg[ret] = 0;
-
-	notice_msg_time = plat_get_ticks_ms();
-}
-
-static const char * const biosfiles_us[] = {
-	"us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"
-};
-static const char * const biosfiles_eu[] = {
-	"eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"
-};
-static const char * const biosfiles_jp[] = {
-	"jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
-};
-
-static const char *find_bios(int *region, const char *cd_fname)
-{
-	int i, count;
-	const char * const *files;
-	FILE *f = NULL;
-	int ret;
-
-	// we need to have config loaded at this point
-	ret = emu_read_config(cd_fname, 0);
-	if (!ret) emu_read_config(NULL, 0);
-
-	if (PicoIn.regionOverride) {
-		*region = PicoIn.regionOverride;
-		lprintf("override region to %s\n", *region != 4 ?
-			(*region == 8 ? "EU" : "JAP") : "USA");
-	}
-
-	if (*region == 4) { // US
-		files = biosfiles_us;
-		count = sizeof(biosfiles_us) / sizeof(char *);
-	} else if (*region == 8) { // EU
-		files = biosfiles_eu;
-		count = sizeof(biosfiles_eu) / sizeof(char *);
-	} else if (*region == 1 || *region == 2) {
-		files = biosfiles_jp;
-		count = sizeof(biosfiles_jp) / sizeof(char *);
-	} else {
-		return 0;
-	}
-
-	/** Check Bios files in home directory */
-	for (i = 0; i < count; i++)
-	{
-		emu_make_path(static_buff, files[i], sizeof(static_buff) - 4);
-		//printf("bios name static_buff: %s\n", static_buff);
-		strcat(static_buff, ".bin");
-		f = fopen(static_buff, "rb");
-		if (f) break;
-
-		static_buff[strlen(static_buff) - 4] = 0;
-		strcat(static_buff, ".zip");
-		f = fopen(static_buff, "rb");
-		if (f) break;
-	}
-
-	/** Check Bios files in <rom path>/bios */
-	if(!f){
-		for (i = 0; i < count; i++)
-		{
-			sprintf(static_buff, "%s/bios/%s", mRomPath, files[i]);
-			printf("static_buff: %s\n", static_buff);
-			strcat(static_buff, ".bin");
-			f = fopen(static_buff, "rb");
-			if (f) break;
-
-			static_buff[strlen(static_buff) - 4] = 0;
-			strcat(static_buff, ".zip");
-			f = fopen(static_buff, "rb");
-			if (f) break;
-		}
-	}
-
-	/** Check Bios files in rom path */
-	if(!f){
-		for (i = 0; i < count; i++)
-		{
-			sprintf(static_buff, "%s/%s", mRomPath, files[i]);
-			printf("static_buff: %s\n", static_buff);
-			strcat(static_buff, ".bin");
-			f = fopen(static_buff, "rb");
-			if (f) break;
-
-			static_buff[strlen(static_buff) - 4] = 0;
-			strcat(static_buff, ".zip");
-			f = fopen(static_buff, "rb");
-			if (f) break;
-		}
-	}
-
-	if (f) {
-		lprintf("using bios: %s\n", static_buff);
-		fclose(f);
-		return static_buff;
-	} else {
-		char* country;
-		char** bios_names;
-		if(*region == 4){ //USA
-			country = "USA";
-			bios_names = biosfiles_us;
-		}
-		else if(*region == 8){ //EU
-			country = "EU";
-			bios_names = biosfiles_eu;
-		}
-		else{ //JAP
-			country = "JAP";
-			bios_names = biosfiles_jp;
-		}
-
-		sprintf(static_buff, "no %s BIOS files found, read docs", country);
-		menu_update_msg(static_buff);
-
-		/** Set notif for BIOS */
-	    char shell_cmd[400];
-	    sprintf(shell_cmd, "%s 0 \"    %s BIOS FILE MISSING^^Connect your FunKey S to ^your computer and copy the^BIOS file in the folder:^    Sega Genesis/bios/^^The file can be called:^ - %s.bin^ - %s.bin^ - %s.bin^ - %s.bin^^For more instructions:^www.funkey-project.com^^Press any button to exit...\"",
-	            SHELL_CMD_NOTIF_SET, country, bios_names[0], bios_names[1], bios_names[2], bios_names[3]);
-	    FILE *fp = popen(shell_cmd, "r");
-	    if (fp == NULL) {
-	        printf("In %s, Failed to run command %s\n", __func__, shell_cmd);
-	    } else {
-	        pclose(fp);
-	    }
-
-	    /** Wait for key press */
-	    SDL_Event event;
-	    while(event.type != SDL_KEYUP && event.type != SDL_QUIT){
-	        while (SDL_PollEvent(&event))
-			SDL_Delay(60);
-	    }
-
-	    /** Clear notif for BIOS */
-	    fp = popen(SHELL_CMD_NOTIF_CLEAR, "r");
-	    if (fp == NULL) {
-	        printf("In %s, Failed to run command %s\n", __func__, SHELL_CMD_NOTIF_CLEAR);
-	    } else {
-	        pclose(fp);
-	    }
-
-	    /** Force clean exit */
-        //engineState = PGS_Quit;
-		emu_finish();
-		plat_finish();
-		plat_target_finish();
-		exit(0);
-		return NULL;
-	}
-}
-
-/* check if the name begins with BIOS name */
-/*
-static int emu_isBios(const char *name)
-{
-	int i;
-	for (i = 0; i < sizeof(biosfiles_us)/sizeof(biosfiles_us[0]); i++)
-		if (strstr(name, biosfiles_us[i]) != NULL) return 1;
-	for (i = 0; i < sizeof(biosfiles_eu)/sizeof(biosfiles_eu[0]); i++)
-		if (strstr(name, biosfiles_eu[i]) != NULL) return 1;
-	for (i = 0; i < sizeof(biosfiles_jp)/sizeof(biosfiles_jp[0]); i++)
-		if (strstr(name, biosfiles_jp[i]) != NULL) return 1;
-	return 0;
-}
-*/
-
-static int extract_text(char *dest, const unsigned char *src, int len, int swab)
-{
-	char *p = dest;
-	int i;
-
-	if (swab) swab = 1;
-
-	for (i = len - 1; i >= 0; i--)
-	{
-		if (src[i^swab] != ' ') break;
-	}
-	len = i + 1;
-
-	for (i = 0; i < len; i++)
-	{
-		unsigned char s = src[i^swab];
-		if (s >= 0x20 && s < 0x7f && s != '#' && s != '|' &&
-			s != '[' && s != ']' && s != '\\')
-		{
-			*p++ = s;
-		}
-		else
-		{
-			sprintf(p, "\\%02x", s);
-			p += 3;
-		}
-	}
-
-	return p - dest;
-}
-
-static char *emu_make_rom_id(const char *fname)
-{
-	static char id_string[3+0xe*3+0x3*3+0x30*3+3];
-	int pos, swab = 1;
-
-	if (PicoIn.AHW & PAHW_MCD) {
-		strcpy(id_string, "CD|");
-		swab = 0;
-	}
-	else if (PicoIn.AHW & PAHW_SMS)
-		strcpy(id_string, "MS|");
-	else	strcpy(id_string, "MD|");
-	pos = 3;
-
-	if (!(PicoIn.AHW & PAHW_SMS)) {
-		pos += extract_text(id_string + pos, media_id_header + 0x80, 0x0e, swab); // serial
-		id_string[pos] = '|'; pos++;
-		pos += extract_text(id_string + pos, media_id_header + 0xf0, 0x03, swab); // region
-		id_string[pos] = '|'; pos++;
-		pos += extract_text(id_string + pos, media_id_header + 0x50, 0x30, swab); // overseas name
-		id_string[pos] = 0;
-		if (pos > 5)
-			return id_string;
-		pos = 3;
-	}
-
-	// can't find name in ROM, use filename
-	fname_ext(id_string + 3, sizeof(id_string) - 3, NULL, NULL, fname);
-
-	return id_string;
-}
-
-// buffer must be at least 150 byte long
-void emu_get_game_name(char *str150)
-{
-	int ret, swab = (PicoIn.AHW & PAHW_MCD) ? 0 : 1;
-	char *s, *d;
-
-	ret = extract_text(str150, media_id_header + 0x50, 0x30, swab); // overseas name
-
-	for (s = d = str150 + 1; s < str150+ret; s++)
-	{
-		if (*s == 0) break;
-		if (*s != ' ' || d[-1] != ' ')
-			*d++ = *s;
-	}
-	*d = 0;
-}
-
-static void system_announce(void)
-{
-	const char *sys_name, *tv_standard, *extra = "";
-	int fps;
-
-	if (PicoIn.AHW & PAHW_SMS) {
-		sys_name = "Master System";
-#ifdef NO_SMS
-		extra = " [no support]";
-#endif
-	} else if (PicoIn.AHW & PAHW_PICO) {
-		sys_name = "Pico";
-	} else if ((PicoIn.AHW & (PAHW_32X|PAHW_MCD)) == (PAHW_32X|PAHW_MCD)) {
-		sys_name = "32X + Mega CD";
-		if ((Pico.m.hardware & 0xc0) == 0x80)
-			sys_name = "32X + Sega CD";
-	} else if (PicoIn.AHW & PAHW_MCD) {
-		sys_name = "Mega CD";
-		if ((Pico.m.hardware & 0xc0) == 0x80)
-			sys_name = "Sega CD";
-	} else if (PicoIn.AHW & PAHW_32X) {
-		sys_name = "32X";
-	} else {
-		sys_name = "MegaDrive";
-		if ((Pico.m.hardware & 0xc0) == 0x80)
-			sys_name = "Genesis";
-	}
-	tv_standard = Pico.m.pal ? "PAL" : "NTSC";
-	fps = Pico.m.pal ? 50 : 60;
-
-	//emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra);
-	printf("\nSystem Announce: %s, %s / %dFPS%s\n", sys_name, tv_standard, fps, extra);
-	printf("PicoIn.AHW = %d, Pico.m.hardware=%d\n", PicoIn.AHW, Pico.m.hardware);
-}
-
-static void do_region_override(const char *media_fname)
-{
-	// we only need to override region if config tells us so
-	int ret = emu_read_config(media_fname, 0);
-	if (!ret) emu_read_config(NULL, 0);
-}
-
-int emu_reload_rom(const char *rom_fname_in)
-{
-	// use setting before rom config is loaded
-	int autoload = g_autostateld_opt;
-	char *rom_fname = NULL;
-	char ext[5];
-	enum media_type_e media_type;
-	int menu_romload_started = 0;
-	char carthw_path[512];
-	int retval = 0;
-
-	lprintf("emu_ReloadRom(%s)\n", rom_fname_in);
-
-	rom_fname = strdup(rom_fname_in);
-	if (rom_fname == NULL)
-		return 0;
-
-	get_ext(rom_fname, ext);
-
-	// early cleanup
-	PicoPatchUnload();
-	if (movie_data) {
-		free(movie_data);
-		movie_data = 0;
-	}
-
-	if (!strcmp(ext, ".gmv"))
-	{
-		// check for both gmv and rom
-		int dummy;
-		FILE *movie_file = fopen(rom_fname, "rb");
-		if (!movie_file) {
-			menu_update_msg("Failed to open movie.");
-			goto out;
-		}
-		fseek(movie_file, 0, SEEK_END);
-		movie_size = ftell(movie_file);
-		fseek(movie_file, 0, SEEK_SET);
-		if (movie_size < 64+3) {
-			menu_update_msg("Invalid GMV file.");
-			fclose(movie_file);
-			goto out;
-		}
-		movie_data = malloc(movie_size);
-		if (movie_data == NULL) {
-			menu_update_msg("low memory.");
-			fclose(movie_file);
-			goto out;
-		}
-		dummy = fread(movie_data, 1, movie_size, movie_file);
-		fclose(movie_file);
-		if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {
-			menu_update_msg("Invalid GMV file.");
-			goto out;
-		}
-		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
-		if (!dummy) {
-			menu_update_msg("Could't find a ROM for movie.");
-			goto out;
-		}
-		get_ext(rom_fname, ext);
-		lprintf("gmv loaded for %s\n", rom_fname);
-	}
-	else if (!strcmp(ext, ".pat"))
-	{
-		int dummy;
-		PicoPatchLoad(rom_fname);
-		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
-		if (!dummy) {
-			menu_update_msg("Could't find a ROM to patch.");
-			goto out;
-		}
-		get_ext(rom_fname, ext);
-	}
-
-	menu_romload_prepare(rom_fname); // also CD load
-	menu_romload_started = 1;
-
-	emu_make_path(carthw_path, "carthw.cfg", sizeof(carthw_path));
-
-	media_type = PicoLoadMedia(rom_fname, carthw_path,
-			find_bios, do_region_override);
-
-	switch (media_type) {
-	case PM_BAD_DETECT:
-		menu_update_msg("Not a ROM/CD img selected.");
-		goto out;
-	case PM_BAD_CD:
-		menu_update_msg("Invalid CD image");
-		goto out;
-	case PM_BAD_CD_NO_BIOS:
-		// find_bios() prints a message
-		goto out;
-	case PM_ERROR:
-		menu_update_msg("Load error");
-		goto out;
-	default:
-		break;
-	}
-
-	// make quirks visible in UI
-	if (PicoIn.quirks & PQUIRK_FORCE_6BTN)
-		currentConfig.input_dev0 = PICO_INPUT_PAD_6BTN;
-
-	menu_romload_end();
-	menu_romload_started = 0;
-
-	if (PicoPatches) {
-		PicoPatchPrepare();
-		PicoPatchApply();
-	}
-
-	// additional movie stuff
-	if (movie_data)
-	{
-		enum input_device indev = (movie_data[0x14] == '6') ?
-			PICO_INPUT_PAD_6BTN : PICO_INPUT_PAD_3BTN;
-		PicoSetInputDevice(0, indev);
-		PicoSetInputDevice(1, indev);
-
-		PicoIn.opt |= POPT_DIS_VDP_FIFO; // no VDP fifo timing
-		if (movie_data[0xF] >= 'A') {
-			if (movie_data[0x16] & 0x80) {
-				PicoIn.regionOverride = 8;
-			} else {
-				PicoIn.regionOverride = 4;
-			}
-			PicoReset();
-			// TODO: bits 6 & 5
-		}
-		movie_data[0x18+30] = 0;
-		emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);
-	}
-	else
-	{
-		system_announce();
-		PicoIn.opt &= ~POPT_DIS_VDP_FIFO;
-	}
-
-	/* Set input map */
-	if (PicoIn.AHW & PAHW_SMS) {
-		printf("plat set sms input\n");
-		plat_set_sms_input();
-	}
-	else{
-		plat_set_genesis_input();
-	}
-
-	strncpy(rom_fname_loaded, rom_fname, sizeof(rom_fname_loaded)-1);
-	rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;
-
-	// load SRAM for this ROM
-	if (currentConfig.EmuOpt & EOPT_EN_SRAM)
-		emu_save_load_game(1, 1);
-
-	// state autoload?
-	if (autoload) {
-		int time, newest = 0, newest_slot = -1;
-		int slot;
-
-		for (slot = 0; slot < 10; slot++) {
-			if (emu_check_save_file(slot, &time)) {
-				if (time > newest) {
-					newest = time;
-					newest_slot = slot;
-				}
-			}
-		}
-
-		if (newest_slot >= 0) {
-			lprintf("autoload slot %d\n", newest_slot);
-			state_slot = newest_slot;
-			emu_save_load_game(1, 0);
-		}
-		else {
-			lprintf("no save to autoload.\n");
-		}
-	}
-
-	retval = 1;
-out:
-	if (menu_romload_started)
-		menu_romload_end();
-	free(rom_fname);
-	return retval;
-}
-
-int emu_swap_cd(const char *fname)
-{
-	enum cd_track_type cd_type;
-	int ret = -1;
-
-	cd_type = PicoCdCheck(fname, NULL);
-	if (cd_type != CT_UNKNOWN)
-		ret = cdd_load(fname, cd_type);
-	if (ret != 0) {
-		menu_update_msg("Load failed, invalid CD image?");
-		return 0;
-	}
-
-	strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1);
-	rom_fname_loaded[sizeof(rom_fname_loaded) - 1] = 0;
-
-	return 1;
-}
-
-// <base dir><end>
-void emu_make_path(char *buff, const char *end, int size)
-{
-	int pos, end_len;
-
-	end_len = strlen(end);
-	pos = plat_get_root_dir(buff, size);
-	strncpy(buff + pos, end, size - pos);
-	buff[size - 1] = 0;
-	if (pos + end_len > size - 1)
-		lprintf("Warning: path truncated: %s\n", buff);
-}
-
-static void make_config_cfg(char *cfg_buff_512)
-{
-	emu_make_path(cfg_buff_512, PicoConfigFile, 512-6);
-	if (config_slot != 0)
-	{
-		char *p = strrchr(cfg_buff_512, '.');
-		if (p == NULL)
-			p = cfg_buff_512 + strlen(cfg_buff_512);
-		sprintf(p, ".%i.cfg", config_slot);
-	}
-	cfg_buff_512[511] = 0;
-}
-
-void emu_prep_defconfig(void)
-{
-	memset(&defaultConfig, 0, sizeof(defaultConfig));
-	defaultConfig.EmuOpt    = EOPT_EN_SRAM | EOPT_EN_SOUND | EOPT_16BPP |
-				  EOPT_GZIP_SAVES | 0x10/*?*/;
-	defaultConfig.s_PicoOpt = POPT_EN_YM2413|POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |
-				  POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX |
-				  POPT_EN_DRC|POPT_ACC_SPRITES |
-				  POPT_EN_32X|POPT_EN_PWM;
-	defaultConfig.s_PsndRate = 44100;
-	defaultConfig.s_PicoRegion = 0; // auto
-	defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP
-	defaultConfig.s_PicoCDBuffers = 0;
-	defaultConfig.s_PicoSndFilterAlpha = 0x10000 * 60 / 100;
-	defaultConfig.confirm_save = EOPT_CONFIRM_SAVE;
-	defaultConfig.Frameskip = -1; // auto
-	defaultConfig.input_dev0 = PICO_INPUT_PAD_3BTN;
-	defaultConfig.input_dev1 = PICO_INPUT_PAD_3BTN;
-	defaultConfig.volume = 50;
-	//defaultConfig.volume = 99;
-	defaultConfig.gamma = 100;
-	defaultConfig.scaling = 0;
-	defaultConfig.turbo_rate = 15;
-	defaultConfig.msh2_khz = PICO_MSH2_HZ / 1000;
-	defaultConfig.ssh2_khz = PICO_SSH2_HZ / 1000;
-	defaultConfig.max_skip = 4;
-
-	// platform specific overrides
-	pemu_prep_defconfig();
-}
-
-void emu_set_defconfig(void)
-{
-	memcpy(&currentConfig, &defaultConfig, sizeof(currentConfig));
-	PicoIn.opt = currentConfig.s_PicoOpt;
-	PicoIn.sndRate = currentConfig.s_PsndRate;
-	PicoIn.regionOverride = currentConfig.s_PicoRegion;
-	PicoIn.autoRgnOrder = currentConfig.s_PicoAutoRgnOrder;
-	PicoIn.sndFilterAlpha = currentConfig.s_PicoSndFilterAlpha;
-}
-
-int emu_read_config(const char *rom_fname, int no_defaults)
-{
-	char cfg[512];
-	int ret;
-
-	if (!no_defaults)
-		emu_set_defconfig();
-
-	if (rom_fname == NULL)
-	{
-		// global config
-		make_config_cfg(cfg);
-		ret = config_readsect(cfg, NULL);
-	}
-	else
-	{
-		char ext[16];
-		int vol;
-
-		if (config_slot != 0)
-			snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);
-		else
-			strcpy(ext, ".cfg");
-
-		fname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext, rom_fname); // gamepath/gamename.cfg
-
-		// read user's config
-		vol = currentConfig.volume;
-		ret = config_readsect(cfg, NULL);
-		currentConfig.volume = vol; // make vol global (bah)
-
-		if (ret != 0)
-		{
-			// read global config, and apply game_def.cfg on top
-			make_config_cfg(cfg); // $HOME/.picodrive/config2.cfg
-			config_readsect(cfg, NULL);
-
-			emu_make_path(cfg, "game_def.cfg", sizeof(cfg)); // $HOME/.picodrive/game_def.cfg
-			ret = config_readsect(cfg, emu_make_rom_id(rom_fname));
-		}
-	}
-
-	pemu_validate_config();
-	PicoIn.overclockM68k = currentConfig.overclock_68k;
-
-	// some sanity checks
-	if (currentConfig.volume < 0 || currentConfig.volume > 99)
-		currentConfig.volume = 50;
-
-	if (ret == 0)
-		config_slot_current = config_slot;
-
-	return (ret == 0);
-}
-
-
-int emu_write_config(int is_game)
-{
-	char cfg[512];
-	int ret, write_lrom = 0;
-
-	if (!is_game)
-	{
-		make_config_cfg(cfg);
-		write_lrom = 1;
-	} else {
-		char ext[16];
-
-		if (config_slot != 0)
-			snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);
-		else
-			strcpy(ext, ".cfg");
-
-		romfname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext);
-	}
-
-	lprintf("emu_write_config: %s ", cfg);
-	ret = config_write(cfg);
-	if (write_lrom) config_writelrom(cfg);
-#ifdef __GP2X__
-	sync();
-#endif
-	lprintf((ret == 0) ? "(ok)\n" : "(failed)\n");
-
-	if (ret == 0) config_slot_current = config_slot;
-	return ret == 0;
-}
-
-
-/* always using built-in font */
-
-#define mk_text_out(name, type, val, topleft, step_x, step_y) \
-void name(int x, int y, const char *text)				\
-{									\
-	int i, l, len = strlen(text);					\
-	type *screen = (type *)(topleft) + x * step_x + y * step_y;	\
-									\
-	for (i = 0; i < len; i++, screen += 8 * step_x)			\
-	{								\
-		for (l = 0; l < 8; l++)					\
-		{							\
-			unsigned char fd = fontdata8x8[text[i] * 8 + l];\
-			type *s = screen + l * step_y;			\
-			if (fd&0x80) s[step_x * 0] = val;		\
-			if (fd&0x40) s[step_x * 1] = val;		\
-			if (fd&0x20) s[step_x * 2] = val;		\
-			if (fd&0x10) s[step_x * 3] = val;		\
-			if (fd&0x08) s[step_x * 4] = val;		\
-			if (fd&0x04) s[step_x * 5] = val;		\
-			if (fd&0x02) s[step_x * 6] = val;		\
-			if (fd&0x01) s[step_x * 7] = val;		\
-		}							\
-	}								\
-}
-
-mk_text_out(emu_text_out8,      unsigned char,    0xf0, g_screen_ptr, 1, g_screen_ppitch)
-mk_text_out(emu_text_out16,     unsigned short, 0xffff, g_screen_ptr, 1, g_screen_ppitch)
-mk_text_out(emu_text_out8_rot,  unsigned char,    0xf0,
-	(char *)g_screen_ptr  + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)
-mk_text_out(emu_text_out16_rot, unsigned short, 0xffff,
-	(short *)g_screen_ptr + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)
-
-#undef mk_text_out
-
-void emu_osd_text16(int x, int y, const char *text)
-{
-	int len = strlen(text) * 8;
-	int i, h;
-
-	len++;
-	if (x + len > g_screen_width)
-		len = g_screen_width - x;
-
-	for (h = 0; h < 8; h++) {
-		unsigned short *p;
-		p = (unsigned short *)g_screen_ptr
-			+ x + g_screen_ppitch * (y + h);
-		for (i = len; i > 0; i--, p++)
-			*p = (*p >> 2) & 0x39e7;
-	}
-	emu_text_out16(x, y, text);
-}
-
-static void update_movie(void)
-{
-	int offs = Pico.m.frame_count*3 + 0x40;
-	if (offs+3 > movie_size) {
-		free(movie_data);
-		movie_data = 0;
-		emu_status_msg("END OF MOVIE.");
-		lprintf("END OF MOVIE.\n");
-	} else {
-		// MXYZ SACB RLDU
-		PicoIn.pad[0] = ~movie_data[offs]   & 0x8f; // ! SCBA RLDU
-		if(!(movie_data[offs]   & 0x10)) PicoIn.pad[0] |= 0x40; // C
-		if(!(movie_data[offs]   & 0x20)) PicoIn.pad[0] |= 0x10; // A
-		if(!(movie_data[offs]   & 0x40)) PicoIn.pad[0] |= 0x20; // B
-		PicoIn.pad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU
-		if(!(movie_data[offs+1] & 0x10)) PicoIn.pad[1] |= 0x40; // C
-		if(!(movie_data[offs+1] & 0x20)) PicoIn.pad[1] |= 0x10; // A
-		if(!(movie_data[offs+1] & 0x40)) PicoIn.pad[1] |= 0x20; // B
-		PicoIn.pad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX
-		if(!(movie_data[offs+2] & 0x01)) PicoIn.pad[0] |= 0x0400; // X
-		if(!(movie_data[offs+2] & 0x04)) PicoIn.pad[0] |= 0x0100; // Z
-		PicoIn.pad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX
-		if(!(movie_data[offs+2] & 0x10)) PicoIn.pad[1] |= 0x0400; // X
-		if(!(movie_data[offs+2] & 0x40)) PicoIn.pad[1] |= 0x0100; // Z
-	}
-}
-
-static int try_ropen_file(const char *fname, int *time)
-{
-	struct stat st;
-	FILE *f;
-
-	f = fopen(fname, "rb");
-	if (f) {
-		if (time != NULL) {
-			*time = 0;
-			if (fstat(fileno(f), &st) == 0)
-				*time = (int)st.st_mtime;
-		}
-		fclose(f);
-		return 1;
-	}
-	return 0;
-}
-
-char *emu_get_save_fname(int load, int is_sram, int slot, int *time)
-{
-	char *saveFname = static_buff;
-	char ext[16];
-
-	if (is_sram)
-	{
-		strcpy(ext, (PicoIn.AHW & PAHW_MCD) ? ".brm" : ".srm");
-		romfname_ext(saveFname, sizeof(static_buff),
-			(PicoIn.AHW & PAHW_MCD) ? "brm"PATH_SEP : "srm"PATH_SEP, ext);
-		if (!load)
-			return saveFname;
-
-		if (try_ropen_file(saveFname, time))
-			return saveFname;
-
-		romfname_ext(saveFname, sizeof(static_buff), NULL, ext);
-		if (try_ropen_file(saveFname, time))
-			return saveFname;
-	}
-	else
-	{
-		const char *ext_main = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds.gz" : ".mds";
-		const char *ext_othr = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds" : ".mds.gz";
-		ext[0] = 0;
-		if (slot > 0 && slot < 10)
-			sprintf(ext, ".%i", slot);
-		strcat(ext, ext_main);
-
-		if (!load) {
-			romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);
-			return saveFname;
-		}
-		else {
-			romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);
-			if (try_ropen_file(saveFname, time))
-				return saveFname;
-
-			romfname_ext(saveFname, sizeof(static_buff), NULL, ext);
-			if (try_ropen_file(saveFname, time))
-				return saveFname;
-
-			// try the other ext
-			ext[0] = 0;
-			if (slot > 0 && slot < 10)
-				sprintf(ext, ".%i", slot);
-			strcat(ext, ext_othr);
-
-			romfname_ext(saveFname, sizeof(static_buff), "mds"PATH_SEP, ext);
-			if (try_ropen_file(saveFname, time))
-				return saveFname;
-		}
-	}
-
-	return NULL;
-}
-
-int emu_check_save_file(int slot, int *time)
-{
-	return emu_get_save_fname(1, 0, slot, time) ? 1 : 0;
-}
-
-int emu_save_load_game_from_file(int load, char *saveFname){
-	int ret = PicoState(saveFname, !load);
-	if (!ret) {
-		//emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");
-	} else {
-		//emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");
-		ret = -1;
-	}
-
-	return ret;
-}
-
-
-int emu_save_load_game(int load, int sram)
-{
-	int ret = 0;
-	char *saveFname;
-
-	// make save filename
-	saveFname = emu_get_save_fname(load, sram, state_slot, NULL);
-	if (saveFname == NULL) {
-		/*if (!sram)
-			emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");*/
-		return -1;
-	}
-
-	lprintf("saveLoad (%i, %i): %s\n", load, sram, saveFname);
-
-	if (sram)
-	{
-		FILE *sramFile;
-		int sram_size;
-		unsigned char *sram_data;
-		int truncate = 1;
-		if (PicoIn.AHW & PAHW_MCD)
-		{
-			if (PicoIn.opt & POPT_EN_MCD_RAMCART) {
-				sram_size = 0x12000;
-				sram_data = Pico.sv.data;
-				if (sram_data)
-					memcpy(sram_data, Pico_mcd->bram, 0x2000);
-			} else {
-				sram_size = 0x2000;
-				sram_data = Pico_mcd->bram;
-				truncate  = 0; // the .brm may contain RAM cart data after normal brm
-			}
-		} else {
-			sram_size = Pico.sv.size;
-			sram_data = Pico.sv.data;
-		}
-		if (sram_data == NULL)
-			return 0; // cart saves forcefully disabled for this game
-
-		if (load)
-		{
-			sramFile = fopen(saveFname, "rb");
-			if (!sramFile)
-				return -1;
-			ret = fread(sram_data, 1, sram_size, sramFile);
-			ret = ret > 0 ? 0 : -1;
-			fclose(sramFile);
-			if ((PicoIn.AHW & PAHW_MCD) && (PicoIn.opt&POPT_EN_MCD_RAMCART))
-				memcpy(Pico_mcd->bram, sram_data, 0x2000);
-		} else {
-			// sram save needs some special processing
-			// see if we have anything to save
-			for (; sram_size > 0; sram_size--)
-				if (sram_data[sram_size-1]) break;
-
-			if (sram_size) {
-				sramFile = fopen(saveFname, truncate ? "wb" : "r+b");
-				if (!sramFile) sramFile = fopen(saveFname, "wb"); // retry
-				if (!sramFile) return -1;
-				ret = fwrite(sram_data, 1, sram_size, sramFile);
-				ret = (ret != sram_size) ? -1 : 0;
-				fclose(sramFile);
-#ifdef __GP2X__
-				sync();
-#endif
-			}
-		}
-		return ret;
-	}
-	else
-	{
-		ret = PicoState(saveFname, !load);
-		if (!ret) {
-#ifdef __GP2X__
-			if (!load) sync();
-#endif
-			//emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");
-		} else {
-			//emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");
-			ret = -1;
-		}
-
-		return ret;
-	}
-}
-
-void emu_set_fastforward(int set_on)
-{
-	static void *set_PsndOut = NULL;
-	static int set_Frameskip, set_EmuOpt, is_on = 0;
-
-	if (set_on && !is_on) {
-		set_PsndOut = PicoIn.sndOut;
-		set_Frameskip = currentConfig.Frameskip;
-		set_EmuOpt = currentConfig.EmuOpt;
-		PicoIn.sndOut = NULL;
-		currentConfig.Frameskip = 8;
-		currentConfig.EmuOpt &= ~4;
-		currentConfig.EmuOpt |= EOPT_NO_FRMLIMIT;
-		is_on = 1;
-		emu_status_msg("FAST FORWARD");
-	}
-	else if (!set_on && is_on) {
-		PicoIn.sndOut = set_PsndOut;
-		currentConfig.Frameskip = set_Frameskip;
-		currentConfig.EmuOpt = set_EmuOpt;
-		PsndRerate(1);
-		is_on = 0;
-		// mainly to unbreak pcm
-		if (PicoIn.AHW & PAHW_MCD)
-			pcd_state_loaded();
-	}
-}
-
-static void emu_tray_open(void)
-{
-	engineState = PGS_TrayMenu;
-}
-
-static void emu_tray_close(void)
-{
-	emu_status_msg("CD tray closed.");
-}
-
-void emu_32x_startup(void)
-{
-	plat_video_toggle_renderer(0, 0); // HACK
-	system_announce();
-}
-
-void emu_reset_game(void)
-{
-	PicoReset();
-	reset_timing = 1;
-}
-
-void run_events_pico(unsigned int events)
-{
-	int lim_x;
-
-	if (events & PEV_PICO_SWINP) {
-		pico_inp_mode++;
-		if (pico_inp_mode > 2)
-			pico_inp_mode = 0;
-		switch (pico_inp_mode) {
-			case 2: emu_status_msg("Input: Pen on Pad"); break;
-			case 1: emu_status_msg("Input: Pen on Storyware"); break;
-			case 0: emu_status_msg("Input: Joystick");
-				PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;
-				break;
-		}
-	}
-	if (events & PEV_PICO_PPREV) {
-		PicoPicohw.page--;
-		if (PicoPicohw.page < 0)
-			PicoPicohw.page = 0;
-		emu_status_msg("Page %i", PicoPicohw.page);
-	}
-	if (events & PEV_PICO_PNEXT) {
-		PicoPicohw.page++;
-		if (PicoPicohw.page > 6)
-			PicoPicohw.page = 6;
-		emu_status_msg("Page %i", PicoPicohw.page);
-	}
-
-	if (pico_inp_mode == 0)
-		return;
-
-	/* handle other input modes */
-	if (PicoIn.pad[0] & 1) pico_pen_y--;
-	if (PicoIn.pad[0] & 2) pico_pen_y++;
-	if (PicoIn.pad[0] & 4) pico_pen_x--;
-	if (PicoIn.pad[0] & 8) pico_pen_x++;
-	PicoIn.pad[0] &= ~0x0f; // release UDLR
-
-	lim_x = (Pico.video.reg[12]&1) ? 319 : 255;
-	if (pico_pen_y < 8)
-		pico_pen_y = 8;
-	if (pico_pen_y > 224 - PICO_PEN_ADJUST_Y)
-		pico_pen_y = 224 - PICO_PEN_ADJUST_Y;
-	if (pico_pen_x < 0)
-		pico_pen_x = 0;
-	if (pico_pen_x > lim_x - PICO_PEN_ADJUST_X)
-		pico_pen_x = lim_x - PICO_PEN_ADJUST_X;
-
-	PicoPicohw.pen_pos[0] = pico_pen_x;
-	if (!(Pico.video.reg[12] & 1))
-		PicoPicohw.pen_pos[0] += pico_pen_x / 4;
-	PicoPicohw.pen_pos[0] += 0x3c;
-	PicoPicohw.pen_pos[1] = pico_inp_mode == 1 ? (0x2f8 + pico_pen_y) : (0x1fc + pico_pen_y);
-}
-
-static void do_turbo(unsigned short *pad, int acts)
-{
-	static int turbo_pad = 0;
-	static unsigned char turbo_cnt[3] = { 0, 0, 0 };
-	int inc = currentConfig.turbo_rate * 2;
-
-	if (acts & 0x1000) {
-		turbo_cnt[0] += inc;
-		if (turbo_cnt[0] >= 60)
-			turbo_pad ^= 0x10, turbo_cnt[0] = 0;
-	}
-	if (acts & 0x2000) {
-		turbo_cnt[1] += inc;
-		if (turbo_cnt[1] >= 60)
-			turbo_pad ^= 0x20, turbo_cnt[1] = 0;
-	}
-	if (acts & 0x4000) {
-		turbo_cnt[2] += inc;
-		if (turbo_cnt[2] >= 60)
-			turbo_pad ^= 0x40, turbo_cnt[2] = 0;
-	}
-	*pad |= turbo_pad & (acts >> 8);
-}
-
-static void run_events_ui(unsigned int which)
-{
-	char shell_cmd[100];
-	FILE *fp;
-	//emu_action_old = emu_action;
-	//printf("New event: %d\n", which);
-
-	if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE))
-	{
-		int do_it = 1;
-		if ( emu_check_save_file(state_slot, NULL) &&
-			(((which & PEV_STATE_LOAD) && (currentConfig.confirm_save & EOPT_CONFIRM_LOAD)) ||
-			 ((which & PEV_STATE_SAVE) && (currentConfig.confirm_save & EOPT_CONFIRM_SAVE))) )
-		{
-			const char *nm;
-			char tmp[64];
-			int keys, len;
-
-			strcpy(tmp, (which & PEV_STATE_LOAD) ? "LOAD STATE? " : "OVERWRITE SAVE? ");
-			len = strlen(tmp);
-			nm = in_get_key_name(-1, -PBTN_MOK);
-			snprintf(tmp + len, sizeof(tmp) - len, "(%s=yes, ", nm);
-			len = strlen(tmp);
-			nm = in_get_key_name(-1, -PBTN_MBACK);
-			snprintf(tmp + len, sizeof(tmp) - len, "%s=no)", nm);
-
-			plat_status_msg_busy_first(tmp);
-
-			in_set_config_int(0, IN_CFG_BLOCKING, 1);
-			while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))
-				;
-			while ( !((keys = in_menu_wait_any(NULL, 50)) & (PBTN_MOK | PBTN_MBACK)))
-				;
-			if (keys & PBTN_MBACK)
-				do_it = 0;
-			while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))
-				;
-			in_set_config_int(0, IN_CFG_BLOCKING, 0);
-			plat_status_msg_clear();
-		}
-		if (do_it) {
-			plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");
-			PicoStateProgressCB = plat_status_msg_busy_next;
-			emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);
-			PicoStateProgressCB = NULL;
-		}
-	}
-	if (which & PEV_SWITCH_RND)
-	{
-		plat_video_toggle_renderer(1, 0);
-	}
-	if (which & PEV_VOL_DOWN)
-	{
-		printf("PEV_VOL_DOWN\r\n");
-		/// ----- Compute new value -----
-		volume_percentage = (volume_percentage < STEP_CHANGE_VOLUME)?
-			0:(volume_percentage-STEP_CHANGE_VOLUME);
-		/// ----- HUD msg ------
-		char txt[100];
-		sprintf(txt, "VOLUME %d%%", volume_percentage);
-		plat_status_msg_busy_first(txt);
-		/// ----- Shell cmd ----
-		sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-	}
-	if (which & PEV_VOL_UP)
-	{
-		printf("PEV_VOL_UP\r\n");
-		/// ----- Compute new value -----
-		volume_percentage = (volume_percentage > 100 - STEP_CHANGE_VOLUME)?
-			100:(volume_percentage+STEP_CHANGE_VOLUME);
-		/// ----- HUD msg ------
-		char txt[100];
-		sprintf(txt, "VOLUME %d%%", volume_percentage);
-		plat_status_msg_busy_first(txt);
-		/// ----- Shell cmd ----
-		sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-	}
-	if (which & PEV_BRIGHT_UP)
-	{
-		printf("PEV_BRIGHT_UP\r\n");
-		/// ----- Compute new value -----
-		brightness_percentage = (brightness_percentage > 100 - STEP_CHANGE_BRIGHTNESS)?
-			100:(brightness_percentage+STEP_CHANGE_BRIGHTNESS);
-		/// ----- HUD msg ------
-		char txt[100];
-		sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage);
-		plat_status_msg_busy_first(txt);
-		/// ----- Shell cmd ----
-		sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-	}
-	if (which & PEV_BRIGHT_DOWN)
-	{
-		printf("PEV_BRIGHT_DOWN\r\n");
-		/// ----- Compute new value -----
-		brightness_percentage = (brightness_percentage < STEP_CHANGE_BRIGHTNESS)?
-			0:(brightness_percentage-STEP_CHANGE_BRIGHTNESS);
-		/// ----- HUD msg ------
-		char txt[100];
-		sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage);
-		plat_status_msg_busy_first(txt);
-		/// ----- Shell cmd ----
-		sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-	}
-	if (which & PEV_AR_FACT_UP)
-	{
-		printf("PEV_AR_FACT_UP\r\n");
-		/// ----- Compute new value -----
-		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
-			aspect_ratio_factor_percent = (aspect_ratio_factor_percent+aspect_ratio_factor_step<100)?
-				aspect_ratio_factor_percent+aspect_ratio_factor_step:100;
-			need_screen_cleared = 1;
-		}
-		else{
-			aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
-		}
-		aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
-		/// ----- HUD msg ------
-		/*char txt[100];
-		sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
-		plat_status_msg_busy_first(txt);*/
-		sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
-			SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-
-        // Save config file
-        configfile_save(cfg_file_rom);
-	}
-	if (which & PEV_AR_FACT_DOWN)
-	{
-		printf("PEV_AR_FACT_DOWN\r\n");
-		/// ----- Compute new value -----
-		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
-			aspect_ratio_factor_percent = (aspect_ratio_factor_percent>aspect_ratio_factor_step)?
-				aspect_ratio_factor_percent-aspect_ratio_factor_step:0;
-			need_screen_cleared = 1;
-		}
-		else{
-			aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
-		}
-		aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
-		/// ----- HUD msg ------
-		/*char txt[100];
-		sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
-		plat_status_msg_busy_first(txt);*/
-		sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
-			SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-
-        // Save config file
-        configfile_save(cfg_file_rom);
-
-	}
-	if (which & PEV_DISPMODE)
-	{
-		printf("PEV_DISPMODE\r\n");
-		/// ----- Compute new value -----
-		aspect_ratio = (aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES;
-		/// ----- HUD msg ------
-		//char txt[100];
-		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
-			//sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
-			sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
-				SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
-		}
-		else{
-			//sprintf(txt, "DISPLAY MODE: %s", aspect_ratio_name[aspect_ratio]);
-			sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: %s\"",
-				SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_name[aspect_ratio]);
-		}
-		//plat_status_msg_busy_first(txt);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-
-        // Save config file
-        configfile_save(cfg_file_rom);
-	}
-	if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT))
-	{
-		if (which & PEV_SSLOT_PREV) {
-			state_slot -= 1;
-			if (state_slot < 0)
-				state_slot = 9;
-		} else {
-			state_slot += 1;
-			if (state_slot > 9)
-				state_slot = 0;
-		}
-
-		emu_status_msg("SAVE SLOT %i [%s]", state_slot,
-			emu_check_save_file(state_slot, NULL) ? "USED" : "FREE");
-	}
-	if (which & PEV_RESET)
-		emu_reset_game();
-	if (which & PEV_MENU)
-		engineState = PGS_Menu;
-}
-
-void emu_update_input(void)
-{
-	static int prev_events = 0;
-	int actions[IN_BINDTYPE_COUNT] = { 0, };
-	int pl_actions[2];
-	int events;
-
-	in_update(actions);
-
-	pl_actions[0] = actions[IN_BINDTYPE_PLAYER12];
-	pl_actions[1] = actions[IN_BINDTYPE_PLAYER12] >> 16;
-
-	PicoIn.pad[0] = pl_actions[0] & 0xfff;
-	PicoIn.pad[1] = pl_actions[1] & 0xfff;
-
-	if (pl_actions[0] & 0x7000)
-		do_turbo(&PicoIn.pad[0], pl_actions[0]);
-	if (pl_actions[1] & 0x7000)
-		do_turbo(&PicoIn.pad[1], pl_actions[1]);
-
-	events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
-
-	// volume is treated in special way and triggered every frame
-	if (events & (PEV_VOL_DOWN|PEV_VOL_UP))
-		plat_update_volume(1, events & PEV_VOL_UP);
-
-	if ((events ^ prev_events) & PEV_FF) {
-		emu_set_fastforward(events & PEV_FF);
-		plat_update_volume(0, 0);
-		reset_timing = 1;
-	}
-
-	events &= ~prev_events;
-
-	/* SMS */
-	if (PicoIn.AHW == PAHW_PICO)
-		run_events_pico(events);
-
-	if (events)
-		run_events_ui(events);
-
-	if (movie_data)
-		update_movie();
-
-	prev_events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
-}
-
-static void mkdir_path(char *path_with_reserve, int pos, const char *name)
-{
-	strcpy(path_with_reserve + pos, name);
-	if (plat_is_dir(path_with_reserve))
-		return;
-	if (mkdir(path_with_reserve, 0777) < 0)
-		lprintf("failed to create: %s\n", path_with_reserve);
-}
-
-void emu_cmn_forced_frame(int no_scale, int do_emu, void *buf)
-{
-	int po_old = PicoIn.opt;
-	int y;
-
-	for (y = 0; y < g_screen_height; y++)
-		memset32((short *)g_screen_ptr + g_screen_ppitch * y, 0,
-			 g_screen_width * 2 / 4);
-
-	PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);
-	PicoIn.opt |= POPT_ACC_SPRITES;
-	if (!no_scale && currentConfig.scaling)
-		PicoIn.opt |= POPT_EN_SOFTSCALE;
-
-	PicoDrawSetOutFormat(PDF_RGB555, 1);
-	PicoDrawSetOutBuf(buf, g_screen_ppitch * 2);
-	Pico.m.dirtyPal = 1;
-	Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;
-	if (do_emu)
-		PicoFrame();
-	else
-		PicoFrameDrawOnly();
-
-	PicoIn.opt = po_old;
-}
-
-int emu_is_segaCD(){
-	return (PicoIn.AHW & PAHW_MCD);
-}
-
-/* Quick save and turn off the console */
-void quick_save_and_poweroff()
-{
-    FILE *fp;
-
-    printf("Save Instant Play file\n");
-
-    /* Send command to cancel any previously scheduled powerdown */
-    fp = popen(SHELL_CMD_POWERDOWN_HANDLE, "r");
-    if (fp == NULL)
-    {
-        /* Countdown is still ticking, so better do nothing
-	   than start writing and get interrupted!
-	*/
-        printf("Failed to cancel scheduled shutdown\n");
-	exit(0);
-    }
-    pclose(fp);
-
-    /* Save  */
-    emu_save_load_game_from_file(0, quick_save_file);
-
-    /* Perform Instant Play save and shutdown */
-    execlp(SHELL_CMD_INSTANT_PLAY, SHELL_CMD_INSTANT_PLAY,
-	"save", prog_name, "-loadStateFile", quick_save_file, mRomName, NULL);
-
-    /* Should not be reached */
-    printf("Failed to perform Instant Play save and shutdown\n");
-
-    /* Exit Emulator */
-    exit(0);
-}
-
-
-void emu_init(void)
-{
-	char path[512];
-	int pos;
-
-#if 0
-	// FIXME: handle through menu, etc
-	FILE *f;
-	f = fopen("32X_M_BIOS.BIN", "rb");
-	p32x_bios_m = malloc(2048);
-	fread(p32x_bios_m, 1, 2048, f);
-	fclose(f);
-	f = fopen("32X_S_BIOS.BIN", "rb");
-	p32x_bios_s = malloc(1024);
-	fread(p32x_bios_s, 1, 1024, f);
-	fclose(f);
-#endif
-
-	/* make dirs for saves */
-	pos = plat_get_root_dir(path, sizeof(path) - 4);
-	mkdir_path(path, pos, "mds");
-	mkdir_path(path, pos, "srm");
-	mkdir_path(path, pos, "brm");
-	mkdir_path(path, pos, "cfg");
-
-	pprof_init();
-
-	make_config_cfg(path);
-	config_readlrom(path);
-
-	PicoInit();
-	PicoIn.osdMessage = plat_status_msg_busy_next;
-	PicoIn.mcdTrayOpen = emu_tray_open;
-	PicoIn.mcdTrayClose = emu_tray_close;
-
-	sndout_init();
-}
-
-void emu_finish(void)
-{
-	// save SRAM
-	if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {
-		emu_save_load_game(0, 1);
-		Pico.sv.changed = 0;
-	}
-
-	if (!(currentConfig.EmuOpt & EOPT_NO_AUTOSVCFG)) {
-		char cfg[512];
-		make_config_cfg(cfg);
-		config_writelrom(cfg);
-#ifdef __GP2X__
-		sync();
-#endif
-	}
-
-	pprof_finish();
-
-	PicoExit();
-	sndout_exit();
-}
-
-static void snd_write_nonblocking(int len)
-{
-	sndout_write_nb(PicoIn.sndOut, len);
-}
-
-void emu_sound_start(void)
-{
-	PicoIn.sndOut = NULL;
-
-	if (currentConfig.EmuOpt & EOPT_EN_SOUND)
-	{
-		int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;
-		/// Hard Bypass Stereo to mono
-		//is_stereo = 0;
-
-		PsndRerate(Pico.m.frame_count ? 1 : 0);
-
-		printf("starting audio: %i len: %i stereo: %i, pal: %i\n",
-			PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);
-		sndout_start(PicoIn.sndRate, is_stereo);
-		PicoIn.writeSound = snd_write_nonblocking;
-		plat_update_volume(0, 0);
-		memset(sndBuffer, 0, sizeof(sndBuffer));
-		PicoIn.sndOut = sndBuffer;
-	}
-}
-
-void emu_sound_stop(void)
-{
-	sndout_stop();
-}
-
-void emu_sound_wait(void)
-{
-	sndout_wait();
-}
-
-static void emu_loop_prep(void)
-{
-	static int pal_old = -1;
-	static int filter_old = -1;
-
-	if (currentConfig.CPUclock != plat_target_cpu_clock_get())
-		plat_target_cpu_clock_set(currentConfig.CPUclock);
-
-	if (Pico.m.pal != pal_old) {
-		plat_target_lcdrate_set(Pico.m.pal);
-		pal_old = Pico.m.pal;
-	}
-
-	if (currentConfig.filter != filter_old) {
-		plat_target_hwfilter_set(currentConfig.filter);
-		filter_old = currentConfig.filter;
-	}
-
-	plat_target_gamma_set(currentConfig.gamma, 0);
-
-	pemu_loop_prep();
-}
-
-/* our tick here is 1 us right now */
-#define ms_to_ticks(x) (unsigned int)(x * 1000)
-#define get_ticks() plat_get_ticks_us()
-#define vsync_delay_x3	3*ms_to_ticks(1)
-
-void emu_loop(void)
-{
-	int frames_done, frames_shown;	/* actual frames for fps counter */
-	int frame_nb = 0;
-	int target_frametime_x3;
-	unsigned int timestamp_x3 = 0;
-	unsigned int timestamp_aim_x3 = 0;
-	unsigned int timestamp_fps_x3 = 0;
-	char *notice_msg = NULL;
-	char fpsbuff[24];
-	int fskip_cnt = 0;
-
-	fpsbuff[0] = 0;
-
-	PicoLoopPrepare();
-
-	plat_video_loop_prepare();
-	emu_loop_prep();
-	pemu_sound_start();
-
-	/* number of ticks per frame */
-	if (Pico.m.pal)
-		target_frametime_x3 = 3 * ms_to_ticks(1000) / 50;
-	else
-		target_frametime_x3 = 3 * ms_to_ticks(1000) / 60;
-
-	reset_timing = 1;
-	frames_done = frames_shown = 0;
-
-	/* loop with resync every 1 sec. */
-	while (engineState == PGS_Running)
-	{
-		int skip = 0;
-		int diff;
-
-		pprof_start(main);
-
-		if (reset_timing) {
-			reset_timing = 0;
-			plat_video_wait_vsync();
-			timestamp_aim_x3 = get_ticks() * 3;
-			timestamp_fps_x3 = timestamp_aim_x3;
-			fskip_cnt = 0;
-		}
-		else if (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) {
-			timestamp_aim_x3 = get_ticks() * 3;
-		}
-
-		timestamp_x3 = get_ticks() * 3;
-
-		// show notice_msg message?
-		if (notice_msg_time != 0)
-		{
-			static int noticeMsgSum;
-			if (timestamp_x3 - ms_to_ticks(notice_msg_time) * 3
-			     > ms_to_ticks(STATUS_MSG_TIMEOUT) * 3)
-			{
-				notice_msg_time = 0;
-				notice_msg = NULL;
-				plat_status_msg_clear();
-			}
-			else {
-				int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];
-				if (sum != noticeMsgSum) {
-					plat_status_msg_clear();
-					noticeMsgSum = sum;
-				}
-				notice_msg = noticeMsg;
-			}
-		}
-
-		// second changed?
-		if (timestamp_x3 - timestamp_fps_x3 >= ms_to_ticks(1000) * 3)
-		{
-
-#define FUNKEY_RESYNCHRONIZE_AUDIO_SECS	(5*60)
-#ifdef FUNKEY_RESYNCHRONIZE_AUDIO_SECS
-			static unsigned int last_resync = 0, cur_sec=0;
-			if(cur_sec++ - last_resync >= FUNKEY_RESYNCHRONIZE_AUDIO_SECS){
-				last_resync = cur_sec;
-
-				/** Resync here (same as quitting ans relaunching loop for now) */
-				printf("Resync sound now\n");
-				emu_sound_stop();
-				pemu_sound_start();
-			}
-#endif //FUNKEY_RESYNCHRONIZE_AUDIO_SECS
-
-#ifdef BENCHMARK
-			static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];
-			if (++bench == 10) {
-				bench = 0;
-				bench_fps_s = bench_fps;
-				bf[bfp++ & 3] = bench_fps;
-				bench_fps = 0;
-			}
-			bench_fps += frames_shown;
-			sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);
-			printf("%s\n", fpsbuff);
-#else
-			if (currentConfig.EmuOpt & EOPT_SHOW_FPS || show_fps_bypass){
-				printf("%02i/%02i  \n", frames_shown, frames_done);
-				snprintf(fpsbuff, 8, "%02i/%02i  ", frames_shown, frames_done);
-			}
-#endif
-			frames_shown = frames_done = 0;
-			timestamp_fps_x3 += ms_to_ticks(1000) * 3;
-		}
-#ifdef PFRAMES
-		sprintf(fpsbuff, "%i", Pico.m.frame_count);
-#endif
-
-		diff = timestamp_aim_x3 - timestamp_x3;
-
-		if (currentConfig.Frameskip >= 0) // frameskip enabled (or 0)
-		{
-			if (fskip_cnt < currentConfig.Frameskip) {
-				fskip_cnt++;
-				skip = 1;
-			}
-			else {
-				fskip_cnt = 0;
-			}
-		}
-		else if (diff < -target_frametime_x3)
-		{
-			/* no time left for this frame - skip */
-			/* limit auto frameskip to max_skip */
-			if (fskip_cnt < currentConfig.max_skip) {
-				fskip_cnt++;
-				skip = 1;
-			}
-			else {
-				fskip_cnt = 0;
-			}
-		} else
-			fskip_cnt = 0;
-
-		// don't go in debt too much
-		while (diff < -target_frametime_x3 * 3) {
-			timestamp_aim_x3 += target_frametime_x3;
-			diff = timestamp_aim_x3 - timestamp_x3;
-		}
-
-        /* Quick save and poweroff */
-        if(mQuickSaveAndPoweroff){
-			quick_save_and_poweroff();
-			mQuickSaveAndPoweroff = 0;
-        }
-
-		emu_update_input();
-		if (skip) {
-			int do_audio = diff > -target_frametime_x3 * 2;
-			PicoIn.skipFrame = do_audio ? 1 : 2;
-			PicoFrame();
-			PicoIn.skipFrame = 0;
-		}
-		else {
-			PicoFrame();
-			pemu_finalize_frame(fpsbuff, notice_msg);
-			frames_shown++;
-		}
-		frames_done++;
-		timestamp_aim_x3 += target_frametime_x3;
-
-		if (!skip && !flip_after_sync)
-			plat_video_flip();
-
-		/* frame limiter */
-		if (!skip && !reset_timing
-		    && !(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT)))
-		{
-			unsigned int timestamp = get_ticks();
-			diff = timestamp_aim_x3 - timestamp * 3;
-
-			// sleep or vsync if we are still too fast
-			if (diff > target_frametime_x3 + vsync_delay_x3 && (currentConfig.EmuOpt & EOPT_VSYNC)) {
-				// we are too fast
-				plat_video_wait_vsync();
-				timestamp = get_ticks();
-				diff = timestamp * 3 - timestamp_aim_x3;
-			}
-			if (diff > target_frametime_x3 + vsync_delay_x3) {
-				// still too fast
-				plat_wait_till_us(timestamp + (diff - target_frametime_x3) / 3);
-			}
-		}
-
-		if (!skip && flip_after_sync)
-			plat_video_flip();
-
-
-		/* FOR SEGA CD */
-		/* Leave some time for the bios to load before starting a quick load at boot */
-		/* (Sega CD doesn't quick load at boot otherwise) */
-		/* Should have a much cleaner implementation thant this fix */
-		if((PicoIn.AHW & PAHW_MCD) && frame_nb >= 120){
-
-			/* Load slot */
-			if(load_state_slot != -1){
-				printf("LOADING FROM SLOT (SEGA CD) %d...\n", load_state_slot+1);
-				char fname[1024];
-				emu_save_load_game(1, 0);
-				printf("LOADED FROM SLOT (SEGA CD) %d\n", load_state_slot+1);
-				load_state_slot = -1;
-			}
-			if(need_quick_load != -1){
-				load_state_file = quick_save_file;
-				need_quick_load = -1;
-			}
-			/* Load file */
-			if(load_state_file != NULL){
-				printf("LOADING FROM FILE (SEGA CD) %s...\n", load_state_file);
-				emu_save_load_game_from_file(1, load_state_file);
-				printf("LOADED FROM SLOT (SEGA CD) %s\n", load_state_file);
-				load_state_file = NULL;
-			}
-		}
-
-		frame_nb++;
-
-		pprof_end(main);
-	}
-
-	emu_set_fastforward(0);
-
-	// save SRAM
-	if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {
-		plat_status_msg_busy_first("Writing SRAM/BRAM...");
-		emu_save_load_game(0, 1);
-		Pico.sv.changed = 0;
-	}
-
-	pemu_loop_end();
-	emu_sound_stop();
-}
+/*
+ * PicoDrive
+ * (C) notaz, 2007-2010
+ *
+ * This work is licensed under the terms of MAME license.
+ * See COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef __GP2X__
+#include <unistd.h>
+#endif
+
+#include "../libpicofe/posix.h"
+#include "../libpicofe/input.h"
+#include "../libpicofe/fonts.h"
+#include "../libpicofe/sndout.h"
+#include "../libpicofe/lprintf.h"
+#include "../libpicofe/plat.h"
+#include "emu.h"
+#include "configfile_fk.h"
+#include "input_pico.h"
+#include "menu_pico.h"
+#include "config_file.h"
+
+#include <pico/pico_int.h>
+#include <pico/patch.h>
+
+#ifdef USE_LIBRETRO_VFS
+#include "file_stream_transforms.h"
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 7
+#pragma GCC diagnostic ignored "-Wformat-truncation"
+#endif
+
+#ifndef _WIN32
+#define PATH_SEP      "/"
+#define PATH_SEP_C    '/'
+#else
+#define PATH_SEP      "\\"
+#define PATH_SEP_C    '\\'
+#endif
+
+#define STATUS_MSG_TIMEOUT 2000
+
+void *g_screen_ptr;
+
+int g_screen_width  = 320;
+int g_screen_height = 240;
+int g_screen_ppitch = 320; // pitch in pixels
+
+const char *PicoConfigFile = "config2.cfg";
+currentConfig_t currentConfig, defaultConfig;
+int state_slot = 0;
+int config_slot = 0, config_slot_current = 0;
+int pico_pen_x = 320/2, pico_pen_y = 240/2;
+int pico_inp_mode;
+int flip_after_sync;
+int engineState = PGS_Menu;
+int show_fps_bypass = 0;
+int need_screen_cleared = 0;
+
+static short __attribute__((aligned(4))) sndBuffer[2*44100/50];
+
+/* tmp buff to reduce stack usage for plats with small stack */
+static char static_buff[1024];
+const char *rom_fname_reload;
+char rom_fname_loaded[1024];
+int reset_timing = 0;
+static unsigned int notice_msg_time;	/* when started showing */
+static char noticeMsg[40];
+
+unsigned char *movie_data = NULL;
+static int movie_size = 0;
+
+
+/* don't use tolower() for easy old glibc binary compatibility */
+static void strlwr_(char *string)
+{
+	char *p;
+	for (p = string; *p; p++)
+		if ('A' <= *p && *p <= 'Z')
+			*p += 'a' - 'A';
+}
+
+static int try_rfn_cut(char *fname)
+{
+	FILE *tmp;
+	char *p;
+
+	p = fname + strlen(fname) - 1;
+	for (; p > fname; p--)
+		if (*p == '.') break;
+	*p = 0;
+
+	if((tmp = fopen(fname, "rb"))) {
+		fclose(tmp);
+		return 1;
+	}
+	return 0;
+}
+
+static void get_ext(const char *file, char *ext)
+{
+	const char *p;
+
+	p = file + strlen(file) - 4;
+	if (p < file) p = file;
+	strncpy(ext, p, 4);
+	ext[4] = 0;
+	strlwr_(ext);
+}
+
+static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext, const char *fname)
+{
+	int prefix_len = 0;
+	const char *p;
+
+	*dst = 0;
+	if (prefix) {
+		/*int len = plat_get_root_dir(dst, dstlen);
+		strcpy(dst + len, prefix);
+		prefix_len = len + strlen(prefix);*/
+
+		/* Saves are in ROM folder */
+		prefix_len = strlen(mRomPath)+1;
+		sprintf(dst, "%s/", mRomPath);
+	}
+
+	p = fname + strlen(fname) - 1;
+	for (; p >= fname && *p != PATH_SEP_C; p--)
+		;
+	p++;
+	strncpy(dst + prefix_len, p, dstlen - prefix_len - 1);
+
+	dst[dstlen - 8] = 0;
+	if ((p = strrchr(dst, '.')) != NULL)
+		dst[p-dst] = 0;
+	if (ext)
+		strcat(dst, ext);
+}
+
+static void romfname_ext(char *dst, int dstlen, const char *prefix, const char *ext)
+{
+	fname_ext(dst, dstlen, prefix, ext, rom_fname_loaded);
+}
+
+void emu_status_msg(const char *format, ...)
+{
+	va_list vl;
+	int ret;
+
+	va_start(vl, format);
+	ret = vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);
+	va_end(vl);
+
+	/* be sure old text gets overwritten */
+	for (; ret < 28; ret++)
+		noticeMsg[ret] = ' ';
+	noticeMsg[ret] = 0;
+
+	notice_msg_time = plat_get_ticks_ms();
+}
+
+static const char * const biosfiles_us[] = {
+	"us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"
+};
+static const char * const biosfiles_eu[] = {
+	"eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"
+};
+static const char * const biosfiles_jp[] = {
+	"jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
+};
+
+static const char *find_bios(int *region, const char *cd_fname)
+{
+	int i, count;
+	const char * const *files;
+	FILE *f = NULL;
+	int ret;
+
+	// we need to have config loaded at this point
+	ret = emu_read_config(cd_fname, 0);
+	if (!ret) emu_read_config(NULL, 0);
+
+	if (PicoIn.regionOverride) {
+		*region = PicoIn.regionOverride;
+		lprintf("override region to %s\n", *region != 4 ?
+			(*region == 8 ? "EU" : "JAP") : "USA");
+	}
+
+	if (*region == 4) { // US
+		files = biosfiles_us;
+		count = sizeof(biosfiles_us) / sizeof(char *);
+	} else if (*region == 8) { // EU
+		files = biosfiles_eu;
+		count = sizeof(biosfiles_eu) / sizeof(char *);
+	} else if (*region == 1 || *region == 2) {
+		files = biosfiles_jp;
+		count = sizeof(biosfiles_jp) / sizeof(char *);
+	} else {
+		return 0;
+	}
+
+	/** Check Bios files in home directory */
+	for (i = 0; i < count; i++)
+	{
+		emu_make_path(static_buff, files[i], sizeof(static_buff) - 4);
+		//printf("bios name static_buff: %s\n", static_buff);
+		strcat(static_buff, ".bin");
+		f = fopen(static_buff, "rb");
+		if (f) break;
+
+		static_buff[strlen(static_buff) - 4] = 0;
+		strcat(static_buff, ".zip");
+		f = fopen(static_buff, "rb");
+		if (f) break;
+	}
+
+	/** Check Bios files in <rom path>/bios */
+	if(!f){
+		for (i = 0; i < count; i++)
+		{
+			sprintf(static_buff, "%s/bios/%s", mRomPath, files[i]);
+			printf("static_buff: %s\n", static_buff);
+			strcat(static_buff, ".bin");
+			f = fopen(static_buff, "rb");
+			if (f) break;
+
+			static_buff[strlen(static_buff) - 4] = 0;
+			strcat(static_buff, ".zip");
+			f = fopen(static_buff, "rb");
+			if (f) break;
+		}
+	}
+
+	/** Check Bios files in rom path */
+	if(!f){
+		for (i = 0; i < count; i++)
+		{
+			sprintf(static_buff, "%s/%s", mRomPath, files[i]);
+			printf("static_buff: %s\n", static_buff);
+			strcat(static_buff, ".bin");
+			f = fopen(static_buff, "rb");
+			if (f) break;
+
+			static_buff[strlen(static_buff) - 4] = 0;
+			strcat(static_buff, ".zip");
+			f = fopen(static_buff, "rb");
+			if (f) break;
+		}
+	}
+
+	if (f) {
+		lprintf("using bios: %s\n", static_buff);
+		fclose(f);
+		return static_buff;
+	} else {
+		char* country;
+		char** bios_names;
+		if(*region == 4){ //USA
+			country = "USA";
+			bios_names = biosfiles_us;
+		}
+		else if(*region == 8){ //EU
+			country = "EU";
+			bios_names = biosfiles_eu;
+		}
+		else{ //JAP
+			country = "JAP";
+			bios_names = biosfiles_jp;
+		}
+
+		sprintf(static_buff, "no %s BIOS files found, read docs", country);
+		menu_update_msg(static_buff);
+
+		/** Set notif for BIOS */
+	    char shell_cmd[400];
+	    sprintf(shell_cmd, "%s 0 \"    %s BIOS FILE MISSING^^Connect your FunKey S to ^your computer and copy the^BIOS file in the folder:^    Sega Genesis/bios/^^The file can be called:^ - %s.bin^ - %s.bin^ - %s.bin^ - %s.bin^^For more instructions:^www.funkey-project.com^^Press any button to exit...\"",
+	            SHELL_CMD_NOTIF_SET, country, bios_names[0], bios_names[1], bios_names[2], bios_names[3]);
+	    FILE *fp = popen(shell_cmd, "r");
+	    if (fp == NULL) {
+	        printf("In %s, Failed to run command %s\n", __func__, shell_cmd);
+	    } else {
+	        pclose(fp);
+	    }
+
+	    /** Wait for key press */
+	    SDL_Event event;
+	    while(event.type != SDL_KEYUP && event.type != SDL_QUIT){
+	        while (SDL_PollEvent(&event))
+			SDL_Delay(60);
+	    }
+
+	    /** Clear notif for BIOS */
+	    fp = popen(SHELL_CMD_NOTIF_CLEAR, "r");
+	    if (fp == NULL) {
+	        printf("In %s, Failed to run command %s\n", __func__, SHELL_CMD_NOTIF_CLEAR);
+	    } else {
+	        pclose(fp);
+	    }
+
+	    /** Force clean exit */
+        //engineState = PGS_Quit;
+		emu_finish();
+		plat_finish();
+		plat_target_finish();
+		exit(0);
+		return NULL;
+	}
+}
+
+/* check if the name begins with BIOS name */
+/*
+static int emu_isBios(const char *name)
+{
+	int i;
+	for (i = 0; i < sizeof(biosfiles_us)/sizeof(biosfiles_us[0]); i++)
+		if (strstr(name, biosfiles_us[i]) != NULL) return 1;
+	for (i = 0; i < sizeof(biosfiles_eu)/sizeof(biosfiles_eu[0]); i++)
+		if (strstr(name, biosfiles_eu[i]) != NULL) return 1;
+	for (i = 0; i < sizeof(biosfiles_jp)/sizeof(biosfiles_jp[0]); i++)
+		if (strstr(name, biosfiles_jp[i]) != NULL) return 1;
+	return 0;
+}
+*/
+
+static int extract_text(char *dest, const unsigned char *src, int len, int swab)
+{
+	char *p = dest;
+	int i;
+
+	if (swab) swab = 1;
+
+	for (i = len - 1; i >= 0; i--)
+	{
+		if (src[i^swab] != ' ') break;
+	}
+	len = i + 1;
+
+	for (i = 0; i < len; i++)
+	{
+		unsigned char s = src[i^swab];
+		if (s >= 0x20 && s < 0x7f && s != '#' && s != '|' &&
+			s != '[' && s != ']' && s != '\\')
+		{
+			*p++ = s;
+		}
+		else
+		{
+			sprintf(p, "\\%02x", s);
+			p += 3;
+		}
+	}
+
+	return p - dest;
+}
+
+static char *emu_make_rom_id(const char *fname)
+{
+	static char id_string[3+0xe*3+0x3*3+0x30*3+3];
+	int pos, swab = 1;
+
+	if (PicoIn.AHW & PAHW_MCD) {
+		strcpy(id_string, "CD|");
+		swab = 0;
+	}
+	else if (PicoIn.AHW & PAHW_SMS)
+		strcpy(id_string, "MS|");
+	else	strcpy(id_string, "MD|");
+	pos = 3;
+
+	if (!(PicoIn.AHW & PAHW_SMS)) {
+		pos += extract_text(id_string + pos, media_id_header + 0x80, 0x0e, swab); // serial
+		id_string[pos] = '|'; pos++;
+		pos += extract_text(id_string + pos, media_id_header + 0xf0, 0x03, swab); // region
+		id_string[pos] = '|'; pos++;
+		pos += extract_text(id_string + pos, media_id_header + 0x50, 0x30, swab); // overseas name
+		id_string[pos] = 0;
+		if (pos > 5)
+			return id_string;
+		pos = 3;
+	}
+
+	// can't find name in ROM, use filename
+	fname_ext(id_string + 3, sizeof(id_string) - 3, NULL, NULL, fname);
+
+	return id_string;
+}
+
+// buffer must be at least 150 byte long
+void emu_get_game_name(char *str150)
+{
+	int ret, swab = (PicoIn.AHW & PAHW_MCD) ? 0 : 1;
+	char *s, *d;
+
+	ret = extract_text(str150, media_id_header + 0x50, 0x30, swab); // overseas name
+
+	for (s = d = str150 + 1; s < str150+ret; s++)
+	{
+		if (*s == 0) break;
+		if (*s != ' ' || d[-1] != ' ')
+			*d++ = *s;
+	}
+	*d = 0;
+}
+
+static void system_announce(void)
+{
+	const char *sys_name, *tv_standard, *extra = "";
+	int fps;
+
+	if (PicoIn.AHW & PAHW_SMS) {
+		sys_name = "Master System";
+		if (Pico.m.hardware & 0x1)
+			sys_name = "Game Gear";
+#ifdef NO_SMS
+		extra = " [no support]";
+#endif
+	} else if (PicoIn.AHW & PAHW_PICO) {
+		sys_name = "Pico";
+	} else if ((PicoIn.AHW & (PAHW_32X|PAHW_MCD)) == (PAHW_32X|PAHW_MCD)) {
+		sys_name = "32X + Mega CD";
+		if ((Pico.m.hardware & 0xc0) == 0x80)
+			sys_name = "32X + Sega CD";
+	} else if (PicoIn.AHW & PAHW_MCD) {
+		sys_name = "Mega CD";
+		if ((Pico.m.hardware & 0xc0) == 0x80)
+			sys_name = "Sega CD";
+	} else if (PicoIn.AHW & PAHW_32X) {
+		sys_name = "32X";
+	} else {
+		sys_name = "MegaDrive";
+		if ((Pico.m.hardware & 0xc0) == 0x80)
+			sys_name = "Genesis";
+	}
+	tv_standard = Pico.m.pal ? "PAL" : "NTSC";
+	fps = Pico.m.pal ? 50 : 60;
+
+	//emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra);
+	printf("\nSystem Announce: %s, %s / %dFPS%s\n", sys_name, tv_standard, fps, extra);
+	printf("PicoIn.AHW = %d, Pico.m.hardware=%d\n", PicoIn.AHW, Pico.m.hardware);
+}
+
+static void do_region_override(const char *media_fname)
+{
+	// we only need to override region if config tells us so
+	int ret = emu_read_config(media_fname, 0);
+	if (!ret) emu_read_config(NULL, 0);
+}
+
+int emu_reload_rom(const char *rom_fname_in)
+{
+	// use setting before rom config is loaded
+	int autoload = g_autostateld_opt;
+	char *rom_fname = NULL;
+	char ext[5];
+	enum media_type_e media_type;
+	int menu_romload_started = 0;
+	char carthw_path[512];
+	int retval = 0;
+
+	lprintf("emu_ReloadRom(%s)\n", rom_fname_in);
+
+	rom_fname = strdup(rom_fname_in);
+	if (rom_fname == NULL)
+		return 0;
+
+	get_ext(rom_fname, ext);
+
+	// early cleanup
+	PicoPatchUnload();
+	if (movie_data) {
+		free(movie_data);
+		movie_data = 0;
+	}
+
+	if (!strcmp(ext, ".gmv"))
+	{
+		// check for both gmv and rom
+		int dummy;
+		FILE *movie_file = fopen(rom_fname, "rb");
+		if (!movie_file) {
+			menu_update_msg("Failed to open movie.");
+			goto out;
+		}
+		fseek(movie_file, 0, SEEK_END);
+		movie_size = ftell(movie_file);
+		fseek(movie_file, 0, SEEK_SET);
+		if (movie_size < 64+3) {
+			menu_update_msg("Invalid GMV file.");
+			fclose(movie_file);
+			goto out;
+		}
+		movie_data = malloc(movie_size);
+		if (movie_data == NULL) {
+			menu_update_msg("low memory.");
+			fclose(movie_file);
+			goto out;
+		}
+		dummy = fread(movie_data, 1, movie_size, movie_file);
+		fclose(movie_file);
+		if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {
+			menu_update_msg("Invalid GMV file.");
+			goto out;
+		}
+		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
+		if (!dummy) {
+			menu_update_msg("Could't find a ROM for movie.");
+			goto out;
+		}
+		get_ext(rom_fname, ext);
+		lprintf("gmv loaded for %s\n", rom_fname);
+	}
+	else if (!strcmp(ext, ".pat"))
+	{
+		int dummy;
+		PicoPatchLoad(rom_fname);
+		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
+		if (!dummy) {
+			menu_update_msg("Could't find a ROM to patch.");
+			goto out;
+		}
+		get_ext(rom_fname, ext);
+	}
+
+	menu_romload_prepare(rom_fname); // also CD load
+	menu_romload_started = 1;
+
+	emu_make_path(carthw_path, "carthw.cfg", sizeof(carthw_path));
+
+	media_type = PicoLoadMedia(rom_fname, carthw_path,
+			find_bios, do_region_override);
+
+	switch (media_type) {
+	case PM_BAD_DETECT:
+		menu_update_msg("Not a ROM/CD img selected.");
+		goto out;
+	case PM_BAD_CD:
+		menu_update_msg("Invalid CD image");
+		goto out;
+	case PM_BAD_CD_NO_BIOS:
+		// find_bios() prints a message
+		goto out;
+	case PM_ERROR:
+		menu_update_msg("Load error");
+		goto out;
+	default:
+		break;
+	}
+
+	// make quirks visible in UI
+	if (PicoIn.quirks & PQUIRK_FORCE_6BTN)
+		currentConfig.input_dev0 = PICO_INPUT_PAD_6BTN;
+
+	menu_romload_end();
+	menu_romload_started = 0;
+
+	if (PicoPatches) {
+		PicoPatchPrepare();
+		PicoPatchApply();
+	}
+
+	// additional movie stuff
+	if (movie_data)
+	{
+		enum input_device indev = (movie_data[0x14] == '6') ?
+			PICO_INPUT_PAD_6BTN : PICO_INPUT_PAD_3BTN;
+		PicoSetInputDevice(0, indev);
+		PicoSetInputDevice(1, indev);
+
+		PicoIn.opt |= POPT_DIS_VDP_FIFO; // no VDP fifo timing
+		if (movie_data[0xF] >= 'A') {
+			if (movie_data[0x16] & 0x80) {
+				PicoIn.regionOverride = 8;
+			} else {
+				PicoIn.regionOverride = 4;
+			}
+			PicoReset();
+			// TODO: bits 6 & 5
+		}
+		movie_data[0x18+30] = 0;
+		emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);
+	}
+	else
+	{
+		system_announce();
+		PicoIn.opt &= ~POPT_DIS_VDP_FIFO;
+	}
+
+	/* Set input map */
+	if (PicoIn.AHW & PAHW_SMS) {
+		printf("plat set sms input\n");
+		plat_set_sms_input();
+	}
+	else{
+		plat_set_genesis_input();
+	}
+
+	strncpy(rom_fname_loaded, rom_fname, sizeof(rom_fname_loaded)-1);
+	rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;
+
+	// load SRAM for this ROM
+	if (currentConfig.EmuOpt & EOPT_EN_SRAM)
+		emu_save_load_game(1, 1);
+
+	// state autoload?
+	if (autoload) {
+		int time, newest = 0, newest_slot = -1;
+		int slot;
+
+		for (slot = 0; slot < 10; slot++) {
+			if (emu_check_save_file(slot, &time)) {
+				if (time > newest) {
+					newest = time;
+					newest_slot = slot;
+				}
+			}
+		}
+
+		if (newest_slot >= 0) {
+			lprintf("autoload slot %d\n", newest_slot);
+			state_slot = newest_slot;
+			emu_save_load_game(1, 0);
+		}
+		else {
+			lprintf("no save to autoload.\n");
+		}
+	}
+
+	retval = 1;
+out:
+	if (menu_romload_started)
+		menu_romload_end();
+	free(rom_fname);
+	return retval;
+}
+
+int emu_swap_cd(const char *fname)
+{
+	enum cd_track_type cd_type;
+	int ret = -1;
+
+	cd_type = PicoCdCheck(fname, NULL);
+	if (cd_type != CT_UNKNOWN)
+		ret = cdd_load(fname, cd_type);
+	if (ret != 0) {
+		menu_update_msg("Load failed, invalid CD image?");
+		return 0;
+	}
+
+	strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1);
+	rom_fname_loaded[sizeof(rom_fname_loaded) - 1] = 0;
+
+	return 1;
+}
+
+// <base dir><end>
+void emu_make_path(char *buff, const char *end, int size)
+{
+	int pos, end_len;
+
+	end_len = strlen(end);
+	pos = plat_get_root_dir(buff, size);
+	strncpy(buff + pos, end, size - pos);
+	buff[size - 1] = 0;
+	if (pos + end_len > size - 1)
+		lprintf("Warning: path truncated: %s\n", buff);
+}
+
+static void make_config_cfg(char *cfg_buff_512)
+{
+	emu_make_path(cfg_buff_512, PicoConfigFile, 512-6);
+	if (config_slot != 0)
+	{
+		char *p = strrchr(cfg_buff_512, '.');
+		if (p == NULL)
+			p = cfg_buff_512 + strlen(cfg_buff_512);
+		sprintf(p, ".%i.cfg", config_slot);
+	}
+	cfg_buff_512[511] = 0;
+}
+
+void emu_prep_defconfig(void)
+{
+	memset(&defaultConfig, 0, sizeof(defaultConfig));
+	defaultConfig.EmuOpt    = EOPT_EN_SRAM | EOPT_EN_SOUND | EOPT_16BPP |
+				  EOPT_GZIP_SAVES | 0x10/*?*/;
+	defaultConfig.s_PicoOpt = POPT_EN_YM2413|POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |
+				  POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX |POPT_EN_GG_LCD |
+				  POPT_EN_DRC|POPT_ACC_SPRITES |
+				  POPT_EN_32X|POPT_EN_PWM;
+	defaultConfig.s_PsndRate = 44100;
+	defaultConfig.s_PicoRegion = 0; // auto
+	defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP
+	defaultConfig.s_PicoCDBuffers = 0;
+	defaultConfig.s_PicoSndFilterAlpha = 0x10000 * 60 / 100;
+	defaultConfig.confirm_save = EOPT_CONFIRM_SAVE;
+	defaultConfig.Frameskip = -1; // auto
+	defaultConfig.input_dev0 = PICO_INPUT_PAD_3BTN;
+	defaultConfig.input_dev1 = PICO_INPUT_PAD_3BTN;
+	defaultConfig.volume = 50;
+	//defaultConfig.volume = 99;
+	defaultConfig.gamma = 100;
+	defaultConfig.scaling = 0;
+	defaultConfig.turbo_rate = 15;
+	defaultConfig.msh2_khz = PICO_MSH2_HZ / 1000;
+	defaultConfig.ssh2_khz = PICO_SSH2_HZ / 1000;
+	defaultConfig.max_skip = 4;
+
+	// platform specific overrides
+	pemu_prep_defconfig();
+}
+
+void emu_set_defconfig(void)
+{
+	memcpy(&currentConfig, &defaultConfig, sizeof(currentConfig));
+	PicoIn.opt = currentConfig.s_PicoOpt;
+	PicoIn.sndRate = currentConfig.s_PsndRate;
+	PicoIn.regionOverride = currentConfig.s_PicoRegion;
+	PicoIn.autoRgnOrder = currentConfig.s_PicoAutoRgnOrder;
+	PicoIn.sndFilterAlpha = currentConfig.s_PicoSndFilterAlpha;
+}
+
+int emu_read_config(const char *rom_fname, int no_defaults)
+{
+	char cfg[512];
+	int ret;
+
+	if (!no_defaults)
+		emu_set_defconfig();
+
+	if (rom_fname == NULL)
+	{
+		// global config
+		make_config_cfg(cfg);
+		ret = config_readsect(cfg, NULL);
+	}
+	else
+	{
+		char ext[16];
+		int vol;
+
+		if (config_slot != 0)
+			snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);
+		else
+			strcpy(ext, ".cfg");
+
+		fname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext, rom_fname); // gamepath/gamename.cfg
+
+		// read user's config
+		vol = currentConfig.volume;
+		ret = config_readsect(cfg, NULL);
+		currentConfig.volume = vol; // make vol global (bah)
+
+		if (ret != 0)
+		{
+			// read global config, and apply game_def.cfg on top
+			make_config_cfg(cfg); // $HOME/.picodrive/config2.cfg
+			config_readsect(cfg, NULL);
+
+			emu_make_path(cfg, "game_def.cfg", sizeof(cfg)); // $HOME/.picodrive/game_def.cfg
+			ret = config_readsect(cfg, emu_make_rom_id(rom_fname));
+		}
+	}
+
+	pemu_validate_config();
+	PicoIn.overclockM68k = currentConfig.overclock_68k;
+
+	// some sanity checks
+	if (currentConfig.volume < 0 || currentConfig.volume > 99)
+		currentConfig.volume = 50;
+
+	if (ret == 0)
+		config_slot_current = config_slot;
+
+	return (ret == 0);
+}
+
+
+int emu_write_config(int is_game)
+{
+	char cfg[512];
+	int ret, write_lrom = 0;
+
+	if (!is_game)
+	{
+		make_config_cfg(cfg);
+		write_lrom = 1;
+	} else {
+		char ext[16];
+
+		if (config_slot != 0)
+			snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);
+		else
+			strcpy(ext, ".cfg");
+
+		romfname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext);
+	}
+
+	lprintf("emu_write_config: %s ", cfg);
+	ret = config_write(cfg);
+	if (write_lrom) config_writelrom(cfg);
+#ifdef __GP2X__
+	sync();
+#endif
+	lprintf((ret == 0) ? "(ok)\n" : "(failed)\n");
+
+	if (ret == 0) config_slot_current = config_slot;
+	return ret == 0;
+}
+
+
+/* always using built-in font */
+
+#define mk_text_out(name, type, val, topleft, step_x, step_y) \
+void name(int x, int y, const char *text)				\
+{									\
+	int i, l, len = strlen(text);					\
+	type *screen = (type *)(topleft) + x * step_x + y * step_y;	\
+									\
+	for (i = 0; i < len; i++, screen += 8 * step_x)			\
+	{								\
+		for (l = 0; l < 8; l++)					\
+		{							\
+			unsigned char fd = fontdata8x8[text[i] * 8 + l];\
+			type *s = screen + l * step_y;			\
+			if (fd&0x80) s[step_x * 0] = val;		\
+			if (fd&0x40) s[step_x * 1] = val;		\
+			if (fd&0x20) s[step_x * 2] = val;		\
+			if (fd&0x10) s[step_x * 3] = val;		\
+			if (fd&0x08) s[step_x * 4] = val;		\
+			if (fd&0x04) s[step_x * 5] = val;		\
+			if (fd&0x02) s[step_x * 6] = val;		\
+			if (fd&0x01) s[step_x * 7] = val;		\
+		}							\
+	}								\
+}
+
+mk_text_out(emu_text_out8,      unsigned char,    0xf0, g_screen_ptr, 1, g_screen_ppitch)
+mk_text_out(emu_text_out16,     unsigned short, 0xffff, g_screen_ptr, 1, g_screen_ppitch)
+mk_text_out(emu_text_out8_rot,  unsigned char,    0xf0,
+	(char *)g_screen_ptr  + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)
+mk_text_out(emu_text_out16_rot, unsigned short, 0xffff,
+	(short *)g_screen_ptr + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)
+
+#undef mk_text_out
+
+void emu_osd_text16(int x, int y, const char *text)
+{
+	int len = strlen(text) * 8;
+	int i, h;
+
+	len++;
+	if (x + len > g_screen_width)
+		len = g_screen_width - x;
+
+	for (h = 0; h < 8; h++) {
+		unsigned short *p;
+		p = (unsigned short *)g_screen_ptr
+			+ x + g_screen_ppitch * (y + h);
+		for (i = len; i > 0; i--, p++)
+			*p = (*p >> 2) & 0x39e7;
+	}
+	emu_text_out16(x, y, text);
+}
+
+static void update_movie(void)
+{
+	int offs = Pico.m.frame_count*3 + 0x40;
+	if (offs+3 > movie_size) {
+		free(movie_data);
+		movie_data = 0;
+		emu_status_msg("END OF MOVIE.");
+		lprintf("END OF MOVIE.\n");
+	} else {
+		// MXYZ SACB RLDU
+		PicoIn.pad[0] = ~movie_data[offs]   & 0x8f; // ! SCBA RLDU
+		if(!(movie_data[offs]   & 0x10)) PicoIn.pad[0] |= 0x40; // C
+		if(!(movie_data[offs]   & 0x20)) PicoIn.pad[0] |= 0x10; // A
+		if(!(movie_data[offs]   & 0x40)) PicoIn.pad[0] |= 0x20; // B
+		PicoIn.pad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU
+		if(!(movie_data[offs+1] & 0x10)) PicoIn.pad[1] |= 0x40; // C
+		if(!(movie_data[offs+1] & 0x20)) PicoIn.pad[1] |= 0x10; // A
+		if(!(movie_data[offs+1] & 0x40)) PicoIn.pad[1] |= 0x20; // B
+		PicoIn.pad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX
+		if(!(movie_data[offs+2] & 0x01)) PicoIn.pad[0] |= 0x0400; // X
+		if(!(movie_data[offs+2] & 0x04)) PicoIn.pad[0] |= 0x0100; // Z
+		PicoIn.pad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX
+		if(!(movie_data[offs+2] & 0x10)) PicoIn.pad[1] |= 0x0400; // X
+		if(!(movie_data[offs+2] & 0x40)) PicoIn.pad[1] |= 0x0100; // Z
+	}
+}
+
+static int try_ropen_file(const char *fname, int *time)
+{
+	struct stat st;
+	FILE *f;
+
+	f = fopen(fname, "rb");
+	if (f) {
+		if (time != NULL) {
+			*time = 0;
+			if (fstat(fileno(f), &st) == 0)
+				*time = (int)st.st_mtime;
+		}
+		fclose(f);
+		return 1;
+	}
+	return 0;
+}
+
+char *emu_get_save_fname(int load, int is_sram, int slot, int *time)
+{
+	char *saveFname = static_buff;
+	char ext[16];
+
+	if (is_sram)
+	{
+		strcpy(ext, (PicoIn.AHW & PAHW_MCD) ? ".brm" : ".srm");
+		romfname_ext(saveFname, sizeof(static_buff),
+			(PicoIn.AHW & PAHW_MCD) ? "brm"PATH_SEP : "srm"PATH_SEP, ext);
+		if (!load)
+			return saveFname;
+
+		if (try_ropen_file(saveFname, time))
+			return saveFname;
+
+		romfname_ext(saveFname, sizeof(static_buff), NULL, ext);
+		if (try_ropen_file(saveFname, time))
+			return saveFname;
+	}
+	else
+	{
+		const char *ext_main = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds.gz" : ".mds";
+		const char *ext_othr = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds" : ".mds.gz";
+		ext[0] = 0;
+		if (slot > 0 && slot < 10)
+			sprintf(ext, ".%i", slot);
+		strcat(ext, ext_main);
+
+		if (!load) {
+			romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);
+			return saveFname;
+		}
+		else {
+			romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);
+			if (try_ropen_file(saveFname, time))
+				return saveFname;
+
+			romfname_ext(saveFname, sizeof(static_buff), NULL, ext);
+			if (try_ropen_file(saveFname, time))
+				return saveFname;
+
+			// try the other ext
+			ext[0] = 0;
+			if (slot > 0 && slot < 10)
+				sprintf(ext, ".%i", slot);
+			strcat(ext, ext_othr);
+
+			romfname_ext(saveFname, sizeof(static_buff), "mds"PATH_SEP, ext);
+			if (try_ropen_file(saveFname, time))
+				return saveFname;
+		}
+	}
+
+	return NULL;
+}
+
+int emu_check_save_file(int slot, int *time)
+{
+	return emu_get_save_fname(1, 0, slot, time) ? 1 : 0;
+}
+
+int emu_save_load_game_from_file(int load, char *saveFname){
+	int ret = PicoState(saveFname, !load);
+	if (!ret) {
+		//emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");
+	} else {
+		//emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+int emu_save_load_game(int load, int sram)
+{
+	int ret = 0;
+	char *saveFname;
+
+	// make save filename
+	saveFname = emu_get_save_fname(load, sram, state_slot, NULL);
+	if (saveFname == NULL) {
+		/*if (!sram)
+			emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");*/
+		return -1;
+	}
+
+	lprintf("saveLoad (%i, %i): %s\n", load, sram, saveFname);
+
+	if (sram)
+	{
+		FILE *sramFile;
+		int sram_size;
+		unsigned char *sram_data;
+		int truncate = 1;
+		if (PicoIn.AHW & PAHW_MCD)
+		{
+			if (PicoIn.opt & POPT_EN_MCD_RAMCART) {
+				sram_size = 0x12000;
+				sram_data = Pico.sv.data;
+				if (sram_data)
+					memcpy(sram_data, Pico_mcd->bram, 0x2000);
+			} else {
+				sram_size = 0x2000;
+				sram_data = Pico_mcd->bram;
+				truncate  = 0; // the .brm may contain RAM cart data after normal brm
+			}
+		} else {
+			sram_size = Pico.sv.size;
+			sram_data = Pico.sv.data;
+		}
+		if (sram_data == NULL)
+			return 0; // cart saves forcefully disabled for this game
+
+		if (load)
+		{
+			sramFile = fopen(saveFname, "rb");
+			if (!sramFile)
+				return -1;
+			ret = fread(sram_data, 1, sram_size, sramFile);
+			ret = ret > 0 ? 0 : -1;
+			fclose(sramFile);
+			if ((PicoIn.AHW & PAHW_MCD) && (PicoIn.opt&POPT_EN_MCD_RAMCART))
+				memcpy(Pico_mcd->bram, sram_data, 0x2000);
+		} else {
+			// sram save needs some special processing
+			// see if we have anything to save
+			for (; sram_size > 0; sram_size--)
+				if (sram_data[sram_size-1]) break;
+
+			if (sram_size) {
+				sramFile = fopen(saveFname, truncate ? "wb" : "r+b");
+				if (!sramFile) sramFile = fopen(saveFname, "wb"); // retry
+				if (!sramFile) return -1;
+				ret = fwrite(sram_data, 1, sram_size, sramFile);
+				ret = (ret != sram_size) ? -1 : 0;
+				fclose(sramFile);
+#ifdef __GP2X__
+				sync();
+#endif
+			}
+		}
+		return ret;
+	}
+	else
+	{
+		ret = PicoState(saveFname, !load);
+		if (!ret) {
+#ifdef __GP2X__
+			if (!load) sync();
+#endif
+			//emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");
+		} else {
+			//emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");
+			ret = -1;
+		}
+
+		return ret;
+	}
+}
+
+void emu_set_fastforward(int set_on)
+{
+	static void *set_PsndOut = NULL;
+	static int set_Frameskip, set_EmuOpt, is_on = 0;
+
+	if (set_on && !is_on) {
+		set_PsndOut = PicoIn.sndOut;
+		set_Frameskip = currentConfig.Frameskip;
+		set_EmuOpt = currentConfig.EmuOpt;
+		PicoIn.sndOut = NULL;
+		currentConfig.Frameskip = 8;
+		currentConfig.EmuOpt &= ~4;
+		currentConfig.EmuOpt |= EOPT_NO_FRMLIMIT;
+		is_on = 1;
+		emu_status_msg("FAST FORWARD");
+	}
+	else if (!set_on && is_on) {
+		PicoIn.sndOut = set_PsndOut;
+		currentConfig.Frameskip = set_Frameskip;
+		currentConfig.EmuOpt = set_EmuOpt;
+		PsndRerate(1);
+		is_on = 0;
+		// mainly to unbreak pcm
+		if (PicoIn.AHW & PAHW_MCD)
+			pcd_state_loaded();
+	}
+}
+
+static void emu_tray_open(void)
+{
+	engineState = PGS_TrayMenu;
+}
+
+static void emu_tray_close(void)
+{
+	emu_status_msg("CD tray closed.");
+}
+
+void emu_32x_startup(void)
+{
+	plat_video_toggle_renderer(0, 0); // HACK
+	system_announce();
+}
+
+void emu_reset_game(void)
+{
+	PicoReset();
+	reset_timing = 1;
+}
+
+void run_events_pico(unsigned int events)
+{
+	int lim_x;
+
+	if (events & PEV_PICO_SWINP) {
+		pico_inp_mode++;
+		if (pico_inp_mode > 2)
+			pico_inp_mode = 0;
+		switch (pico_inp_mode) {
+			case 2: emu_status_msg("Input: Pen on Pad"); break;
+			case 1: emu_status_msg("Input: Pen on Storyware"); break;
+			case 0: emu_status_msg("Input: Joystick");
+				PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;
+				break;
+		}
+	}
+	if (events & PEV_PICO_PPREV) {
+		PicoPicohw.page--;
+		if (PicoPicohw.page < 0)
+			PicoPicohw.page = 0;
+		emu_status_msg("Page %i", PicoPicohw.page);
+	}
+	if (events & PEV_PICO_PNEXT) {
+		PicoPicohw.page++;
+		if (PicoPicohw.page > 6)
+			PicoPicohw.page = 6;
+		emu_status_msg("Page %i", PicoPicohw.page);
+	}
+
+	if (pico_inp_mode == 0)
+		return;
+
+	/* handle other input modes */
+	if (PicoIn.pad[0] & 1) pico_pen_y--;
+	if (PicoIn.pad[0] & 2) pico_pen_y++;
+	if (PicoIn.pad[0] & 4) pico_pen_x--;
+	if (PicoIn.pad[0] & 8) pico_pen_x++;
+	PicoIn.pad[0] &= ~0x0f; // release UDLR
+
+	lim_x = (Pico.video.reg[12]&1) ? 319 : 255;
+	if (pico_pen_y < 8)
+		pico_pen_y = 8;
+	if (pico_pen_y > 224 - PICO_PEN_ADJUST_Y)
+		pico_pen_y = 224 - PICO_PEN_ADJUST_Y;
+	if (pico_pen_x < 0)
+		pico_pen_x = 0;
+	if (pico_pen_x > lim_x - PICO_PEN_ADJUST_X)
+		pico_pen_x = lim_x - PICO_PEN_ADJUST_X;
+
+	PicoPicohw.pen_pos[0] = pico_pen_x;
+	if (!(Pico.video.reg[12] & 1))
+		PicoPicohw.pen_pos[0] += pico_pen_x / 4;
+	PicoPicohw.pen_pos[0] += 0x3c;
+	PicoPicohw.pen_pos[1] = pico_inp_mode == 1 ? (0x2f8 + pico_pen_y) : (0x1fc + pico_pen_y);
+}
+
+static void do_turbo(unsigned short *pad, int acts)
+{
+	static int turbo_pad = 0;
+	static unsigned char turbo_cnt[3] = { 0, 0, 0 };
+	int inc = currentConfig.turbo_rate * 2;
+
+	if (acts & 0x1000) {
+		turbo_cnt[0] += inc;
+		if (turbo_cnt[0] >= 60)
+			turbo_pad ^= 0x10, turbo_cnt[0] = 0;
+	}
+	if (acts & 0x2000) {
+		turbo_cnt[1] += inc;
+		if (turbo_cnt[1] >= 60)
+			turbo_pad ^= 0x20, turbo_cnt[1] = 0;
+	}
+	if (acts & 0x4000) {
+		turbo_cnt[2] += inc;
+		if (turbo_cnt[2] >= 60)
+			turbo_pad ^= 0x40, turbo_cnt[2] = 0;
+	}
+	*pad |= turbo_pad & (acts >> 8);
+}
+
+static void run_events_ui(unsigned int which)
+{
+	char shell_cmd[100];
+	FILE *fp;
+	//emu_action_old = emu_action;
+	//printf("New event: %d\n", which);
+
+	if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE))
+	{
+		int do_it = 1;
+		if ( emu_check_save_file(state_slot, NULL) &&
+			(((which & PEV_STATE_LOAD) && (currentConfig.confirm_save & EOPT_CONFIRM_LOAD)) ||
+			 ((which & PEV_STATE_SAVE) && (currentConfig.confirm_save & EOPT_CONFIRM_SAVE))) )
+		{
+			const char *nm;
+			char tmp[64];
+			int keys, len;
+
+			strcpy(tmp, (which & PEV_STATE_LOAD) ? "LOAD STATE? " : "OVERWRITE SAVE? ");
+			len = strlen(tmp);
+			nm = in_get_key_name(-1, -PBTN_MOK);
+			snprintf(tmp + len, sizeof(tmp) - len, "(%s=yes, ", nm);
+			len = strlen(tmp);
+			nm = in_get_key_name(-1, -PBTN_MBACK);
+			snprintf(tmp + len, sizeof(tmp) - len, "%s=no)", nm);
+
+			plat_status_msg_busy_first(tmp);
+
+			in_set_config_int(0, IN_CFG_BLOCKING, 1);
+			while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))
+				;
+			while ( !((keys = in_menu_wait_any(NULL, 50)) & (PBTN_MOK | PBTN_MBACK)))
+				;
+			if (keys & PBTN_MBACK)
+				do_it = 0;
+			while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))
+				;
+			in_set_config_int(0, IN_CFG_BLOCKING, 0);
+			plat_status_msg_clear();
+		}
+		if (do_it) {
+			plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");
+			PicoStateProgressCB = plat_status_msg_busy_next;
+			emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);
+			PicoStateProgressCB = NULL;
+		}
+	}
+	if (which & PEV_SWITCH_RND)
+	{
+		plat_video_toggle_renderer(1, 0);
+	}
+	if (which & PEV_VOL_DOWN)
+	{
+		printf("PEV_VOL_DOWN\r\n");
+		/// ----- Compute new value -----
+		volume_percentage = (volume_percentage < STEP_CHANGE_VOLUME)?
+			0:(volume_percentage-STEP_CHANGE_VOLUME);
+		/// ----- HUD msg ------
+		char txt[100];
+		sprintf(txt, "VOLUME %d%%", volume_percentage);
+		plat_status_msg_busy_first(txt);
+		/// ----- Shell cmd ----
+		sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+	}
+	if (which & PEV_VOL_UP)
+	{
+		printf("PEV_VOL_UP\r\n");
+		/// ----- Compute new value -----
+		volume_percentage = (volume_percentage > 100 - STEP_CHANGE_VOLUME)?
+			100:(volume_percentage+STEP_CHANGE_VOLUME);
+		/// ----- HUD msg ------
+		char txt[100];
+		sprintf(txt, "VOLUME %d%%", volume_percentage);
+		plat_status_msg_busy_first(txt);
+		/// ----- Shell cmd ----
+		sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+	}
+	if (which & PEV_BRIGHT_UP)
+	{
+		printf("PEV_BRIGHT_UP\r\n");
+		/// ----- Compute new value -----
+		brightness_percentage = (brightness_percentage > 100 - STEP_CHANGE_BRIGHTNESS)?
+			100:(brightness_percentage+STEP_CHANGE_BRIGHTNESS);
+		/// ----- HUD msg ------
+		char txt[100];
+		sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage);
+		plat_status_msg_busy_first(txt);
+		/// ----- Shell cmd ----
+		sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+	}
+	if (which & PEV_BRIGHT_DOWN)
+	{
+		printf("PEV_BRIGHT_DOWN\r\n");
+		/// ----- Compute new value -----
+		brightness_percentage = (brightness_percentage < STEP_CHANGE_BRIGHTNESS)?
+			0:(brightness_percentage-STEP_CHANGE_BRIGHTNESS);
+		/// ----- HUD msg ------
+		char txt[100];
+		sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage);
+		plat_status_msg_busy_first(txt);
+		/// ----- Shell cmd ----
+		sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+	}
+	if (which & PEV_AR_FACT_UP)
+	{
+		printf("PEV_AR_FACT_UP\r\n");
+		/// ----- Compute new value -----
+		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
+			aspect_ratio_factor_percent = (aspect_ratio_factor_percent+aspect_ratio_factor_step<100)?
+				aspect_ratio_factor_percent+aspect_ratio_factor_step:100;
+			need_screen_cleared = 1;
+		}
+		else{
+			aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
+		}
+		aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
+		/// ----- HUD msg ------
+		/*char txt[100];
+		sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
+		plat_status_msg_busy_first(txt);*/
+		sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
+			SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+
+        // Save config file
+        configfile_save(cfg_file_rom);
+	}
+	if (which & PEV_AR_FACT_DOWN)
+	{
+		printf("PEV_AR_FACT_DOWN\r\n");
+		/// ----- Compute new value -----
+		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
+			aspect_ratio_factor_percent = (aspect_ratio_factor_percent>aspect_ratio_factor_step)?
+				aspect_ratio_factor_percent-aspect_ratio_factor_step:0;
+			need_screen_cleared = 1;
+		}
+		else{
+			aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
+		}
+		aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
+		/// ----- HUD msg ------
+		/*char txt[100];
+		sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
+		plat_status_msg_busy_first(txt);*/
+		sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
+			SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+
+        // Save config file
+        configfile_save(cfg_file_rom);
+
+	}
+	if (which & PEV_DISPMODE)
+	{
+		printf("PEV_DISPMODE\r\n");
+		/// ----- Compute new value -----
+		aspect_ratio = (aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES;
+		/// ----- HUD msg ------
+		//char txt[100];
+		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
+			//sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
+			sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
+				SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
+		}
+		else{
+			//sprintf(txt, "DISPLAY MODE: %s", aspect_ratio_name[aspect_ratio]);
+			sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: %s\"",
+				SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_name[aspect_ratio]);
+		}
+		//plat_status_msg_busy_first(txt);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+
+        // Save config file
+        configfile_save(cfg_file_rom);
+	}
+	if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT))
+	{
+		if (which & PEV_SSLOT_PREV) {
+			state_slot -= 1;
+			if (state_slot < 0)
+				state_slot = 9;
+		} else {
+			state_slot += 1;
+			if (state_slot > 9)
+				state_slot = 0;
+		}
+
+		emu_status_msg("SAVE SLOT %i [%s]", state_slot,
+			emu_check_save_file(state_slot, NULL) ? "USED" : "FREE");
+	}
+	if (which & PEV_RESET)
+		emu_reset_game();
+	if (which & PEV_MENU)
+		engineState = PGS_Menu;
+}
+
+void emu_update_input(void)
+{
+	static int prev_events = 0;
+	int actions[IN_BINDTYPE_COUNT] = { 0, };
+	int pl_actions[2];
+	int events;
+
+	in_update(actions);
+
+	pl_actions[0] = actions[IN_BINDTYPE_PLAYER12];
+	pl_actions[1] = actions[IN_BINDTYPE_PLAYER12] >> 16;
+
+	PicoIn.pad[0] = pl_actions[0] & 0xfff;
+	PicoIn.pad[1] = pl_actions[1] & 0xfff;
+
+	if (pl_actions[0] & 0x7000)
+		do_turbo(&PicoIn.pad[0], pl_actions[0]);
+	if (pl_actions[1] & 0x7000)
+		do_turbo(&PicoIn.pad[1], pl_actions[1]);
+
+	events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
+
+	// volume is treated in special way and triggered every frame
+	if (events & (PEV_VOL_DOWN|PEV_VOL_UP))
+		plat_update_volume(1, events & PEV_VOL_UP);
+
+	if ((events ^ prev_events) & PEV_FF) {
+		emu_set_fastforward(events & PEV_FF);
+		plat_update_volume(0, 0);
+		reset_timing = 1;
+	}
+
+	events &= ~prev_events;
+
+	/* SMS */
+	if (PicoIn.AHW == PAHW_PICO)
+		run_events_pico(events);
+
+	if (events)
+		run_events_ui(events);
+
+	if (movie_data)
+		update_movie();
+
+	prev_events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
+}
+
+static void mkdir_path(char *path_with_reserve, int pos, const char *name)
+{
+	strcpy(path_with_reserve + pos, name);
+	if (plat_is_dir(path_with_reserve))
+		return;
+	if (mkdir(path_with_reserve, 0777) < 0)
+		lprintf("failed to create: %s\n", path_with_reserve);
+}
+
+void emu_cmn_forced_frame(int no_scale, int do_emu, void *buf)
+{
+	int po_old = PicoIn.opt;
+	int y;
+
+	for (y = 0; y < g_screen_height; y++)
+		memset32((short *)g_screen_ptr + g_screen_ppitch * y, 0,
+			 g_screen_width * 2 / 4);
+
+	PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);
+	PicoIn.opt |= POPT_ACC_SPRITES;
+	if (!no_scale && currentConfig.scaling)
+		PicoIn.opt |= POPT_EN_SOFTSCALE;
+
+	PicoDrawSetOutFormat(PDF_RGB555, 1);
+	PicoDrawSetOutBuf(buf, g_screen_ppitch * 2);
+	Pico.m.dirtyPal = 1;
+	Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;
+	if (do_emu)
+		PicoFrame();
+	else
+		PicoFrameDrawOnly();
+
+	PicoIn.opt = po_old;
+}
+
+int emu_is_segaCD(){
+	return (PicoIn.AHW & PAHW_MCD);
+}
+
+/* Quick save and turn off the console */
+void quick_save_and_poweroff()
+{
+    FILE *fp;
+
+    printf("Save Instant Play file\n");
+
+    /* Send command to cancel any previously scheduled powerdown */
+    fp = popen(SHELL_CMD_POWERDOWN_HANDLE, "r");
+    if (fp == NULL)
+    {
+        /* Countdown is still ticking, so better do nothing
+	   than start writing and get interrupted!
+	*/
+        printf("Failed to cancel scheduled shutdown\n");
+	exit(0);
+    }
+    pclose(fp);
+
+    /* Save  */
+    emu_save_load_game_from_file(0, quick_save_file);
+
+    /* Perform Instant Play save and shutdown */
+    execlp(SHELL_CMD_INSTANT_PLAY, SHELL_CMD_INSTANT_PLAY,
+	"save", prog_name, "-loadStateFile", quick_save_file, mRomName, NULL);
+
+    /* Should not be reached */
+    printf("Failed to perform Instant Play save and shutdown\n");
+
+    /* Exit Emulator */
+    exit(0);
+}
+
+
+void emu_init(void)
+{
+	char path[512];
+	int pos;
+
+#if 0
+	// FIXME: handle through menu, etc
+	FILE *f;
+	f = fopen("32X_M_BIOS.BIN", "rb");
+	p32x_bios_m = malloc(2048);
+	fread(p32x_bios_m, 1, 2048, f);
+	fclose(f);
+	f = fopen("32X_S_BIOS.BIN", "rb");
+	p32x_bios_s = malloc(1024);
+	fread(p32x_bios_s, 1, 1024, f);
+	fclose(f);
+#endif
+
+	/* make dirs for saves */
+	pos = plat_get_root_dir(path, sizeof(path) - 4);
+	mkdir_path(path, pos, "mds");
+	mkdir_path(path, pos, "srm");
+	mkdir_path(path, pos, "brm");
+	mkdir_path(path, pos, "cfg");
+
+	pprof_init();
+
+	make_config_cfg(path);
+	config_readlrom(path);
+
+	PicoInit();
+	PicoIn.osdMessage = plat_status_msg_busy_next;
+	PicoIn.mcdTrayOpen = emu_tray_open;
+	PicoIn.mcdTrayClose = emu_tray_close;
+
+	sndout_init();
+}
+
+void emu_finish(void)
+{
+	// save SRAM
+	if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {
+		emu_save_load_game(0, 1);
+		Pico.sv.changed = 0;
+	}
+
+	if (!(currentConfig.EmuOpt & EOPT_NO_AUTOSVCFG)) {
+		char cfg[512];
+		make_config_cfg(cfg);
+		config_writelrom(cfg);
+#ifdef __GP2X__
+		sync();
+#endif
+	}
+
+	pprof_finish();
+
+	PicoExit();
+	sndout_exit();
+}
+
+static void snd_write_nonblocking(int len)
+{
+	sndout_write_nb(PicoIn.sndOut, len);
+}
+
+void emu_sound_start(void)
+{
+	PicoIn.sndOut = NULL;
+
+	if (currentConfig.EmuOpt & EOPT_EN_SOUND)
+	{
+		int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;
+		/// Hard Bypass Stereo to mono
+		//is_stereo = 0;
+
+		PsndRerate(Pico.m.frame_count ? 1 : 0);
+
+		printf("starting audio: %i len: %i stereo: %i, pal: %i\n",
+			PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);
+		sndout_start(PicoIn.sndRate, is_stereo);
+		PicoIn.writeSound = snd_write_nonblocking;
+		plat_update_volume(0, 0);
+		memset(sndBuffer, 0, sizeof(sndBuffer));
+		PicoIn.sndOut = sndBuffer;
+	}
+}
+
+void emu_sound_stop(void)
+{
+	sndout_stop();
+}
+
+void emu_sound_wait(void)
+{
+	sndout_wait();
+}
+
+static void emu_loop_prep(void)
+{
+	static int pal_old = -1;
+	static int filter_old = -1;
+
+	if (currentConfig.CPUclock != plat_target_cpu_clock_get())
+		plat_target_cpu_clock_set(currentConfig.CPUclock);
+
+	if (Pico.m.pal != pal_old) {
+		plat_target_lcdrate_set(Pico.m.pal);
+		pal_old = Pico.m.pal;
+	}
+
+	if (currentConfig.filter != filter_old) {
+		plat_target_hwfilter_set(currentConfig.filter);
+		filter_old = currentConfig.filter;
+	}
+
+	plat_target_gamma_set(currentConfig.gamma, 0);
+
+	pemu_loop_prep();
+}
+
+/* our tick here is 1 us right now */
+#define ms_to_ticks(x) (unsigned int)(x * 1000)
+#define get_ticks() plat_get_ticks_us()
+#define vsync_delay_x3	3*ms_to_ticks(1)
+
+void emu_loop(void)
+{
+	int frames_done, frames_shown;	/* actual frames for fps counter */
+	int frame_nb = 0;
+	int target_frametime_x3;
+	unsigned int timestamp_x3 = 0;
+	unsigned int timestamp_aim_x3 = 0;
+	unsigned int timestamp_fps_x3 = 0;
+	char *notice_msg = NULL;
+	char fpsbuff[24];
+	int fskip_cnt = 0;
+
+	fpsbuff[0] = 0;
+
+	PicoLoopPrepare();
+
+	plat_video_loop_prepare();
+	emu_loop_prep();
+	pemu_sound_start();
+
+	/* number of ticks per frame */
+	if (Pico.m.pal)
+		target_frametime_x3 = 3 * ms_to_ticks(1000) / 50;
+	else
+		target_frametime_x3 = 3 * ms_to_ticks(1000) / 60;
+
+	reset_timing = 1;
+	frames_done = frames_shown = 0;
+
+	/* loop with resync every 1 sec. */
+	while (engineState == PGS_Running)
+	{
+		int skip = 0;
+		int diff;
+
+		pprof_start(main);
+
+		if (reset_timing) {
+			reset_timing = 0;
+			plat_video_wait_vsync();
+			timestamp_aim_x3 = get_ticks() * 3;
+			timestamp_fps_x3 = timestamp_aim_x3;
+			fskip_cnt = 0;
+		}
+		else if (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) {
+			timestamp_aim_x3 = get_ticks() * 3;
+		}
+
+		timestamp_x3 = get_ticks() * 3;
+
+		// show notice_msg message?
+		if (notice_msg_time != 0)
+		{
+			static int noticeMsgSum;
+			if (timestamp_x3 - ms_to_ticks(notice_msg_time) * 3
+			     > ms_to_ticks(STATUS_MSG_TIMEOUT) * 3)
+			{
+				notice_msg_time = 0;
+				notice_msg = NULL;
+				plat_status_msg_clear();
+			}
+			else {
+				int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];
+				if (sum != noticeMsgSum) {
+					plat_status_msg_clear();
+					noticeMsgSum = sum;
+				}
+				notice_msg = noticeMsg;
+			}
+		}
+
+		// second changed?
+		if (timestamp_x3 - timestamp_fps_x3 >= ms_to_ticks(1000) * 3)
+		{
+
+#define FUNKEY_RESYNCHRONIZE_AUDIO_SECS	(5*60)
+#ifdef FUNKEY_RESYNCHRONIZE_AUDIO_SECS
+			static unsigned int last_resync = 0, cur_sec=0;
+			if(cur_sec++ - last_resync >= FUNKEY_RESYNCHRONIZE_AUDIO_SECS){
+				last_resync = cur_sec;
+
+				/** Resync here (same as quitting ans relaunching loop for now) */
+				printf("Resync sound now\n");
+				emu_sound_stop();
+				pemu_sound_start();
+			}
+#endif //FUNKEY_RESYNCHRONIZE_AUDIO_SECS
+
+#ifdef BENCHMARK
+			static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];
+			if (++bench == 10) {
+				bench = 0;
+				bench_fps_s = bench_fps;
+				bf[bfp++ & 3] = bench_fps;
+				bench_fps = 0;
+			}
+			bench_fps += frames_shown;
+			sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);
+			printf("%s\n", fpsbuff);
+#else
+			if (currentConfig.EmuOpt & EOPT_SHOW_FPS || show_fps_bypass){
+				printf("%02i/%02i  \n", frames_shown, frames_done);
+				snprintf(fpsbuff, 8, "%02i/%02i  ", frames_shown, frames_done);
+			}
+#endif
+			frames_shown = frames_done = 0;
+			timestamp_fps_x3 += ms_to_ticks(1000) * 3;
+		}
+#ifdef PFRAMES
+		sprintf(fpsbuff, "%i", Pico.m.frame_count);
+#endif
+
+		diff = timestamp_aim_x3 - timestamp_x3;
+
+		if (currentConfig.Frameskip >= 0) // frameskip enabled (or 0)
+		{
+			if (fskip_cnt < currentConfig.Frameskip) {
+				fskip_cnt++;
+				skip = 1;
+			}
+			else {
+				fskip_cnt = 0;
+			}
+		}
+		else if (diff < -target_frametime_x3)
+		{
+			/* no time left for this frame - skip */
+			/* limit auto frameskip to max_skip */
+			if (fskip_cnt < currentConfig.max_skip) {
+				fskip_cnt++;
+				skip = 1;
+			}
+			else {
+				fskip_cnt = 0;
+			}
+		} else
+			fskip_cnt = 0;
+
+		// don't go in debt too much
+		while (diff < -target_frametime_x3 * 3) {
+			timestamp_aim_x3 += target_frametime_x3;
+			diff = timestamp_aim_x3 - timestamp_x3;
+		}
+
+        /* Quick save and poweroff */
+        if(mQuickSaveAndPoweroff){
+			quick_save_and_poweroff();
+			mQuickSaveAndPoweroff = 0;
+        }
+
+		emu_update_input();
+		if (skip) {
+			int do_audio = diff > -target_frametime_x3 * 2;
+			PicoIn.skipFrame = do_audio ? 1 : 2;
+			PicoFrame();
+			PicoIn.skipFrame = 0;
+		}
+		else {
+			PicoFrame();
+			pemu_finalize_frame(fpsbuff, notice_msg);
+			frames_shown++;
+		}
+		frames_done++;
+		timestamp_aim_x3 += target_frametime_x3;
+
+		if (!skip && !flip_after_sync)
+			plat_video_flip();
+
+		/* frame limiter */
+		if (!skip && !reset_timing
+		    && !(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT)))
+		{
+			unsigned int timestamp = get_ticks();
+			diff = timestamp_aim_x3 - timestamp * 3;
+
+			// sleep or vsync if we are still too fast
+			if (diff > target_frametime_x3 + vsync_delay_x3 && (currentConfig.EmuOpt & EOPT_VSYNC)) {
+				// we are too fast
+				plat_video_wait_vsync();
+				timestamp = get_ticks();
+				diff = timestamp * 3 - timestamp_aim_x3;
+			}
+			if (diff > target_frametime_x3 + vsync_delay_x3) {
+				// still too fast
+				plat_wait_till_us(timestamp + (diff - target_frametime_x3) / 3);
+			}
+		}
+
+		if (!skip && flip_after_sync)
+			plat_video_flip();
+
+
+		/* FOR SEGA CD */
+		/* Leave some time for the bios to load before starting a quick load at boot */
+		/* (Sega CD doesn't quick load at boot otherwise) */
+		/* Should have a much cleaner implementation thant this fix */
+		if((PicoIn.AHW & PAHW_MCD) && frame_nb >= 120){
+
+			/* Load slot */
+			if(load_state_slot != -1){
+				printf("LOADING FROM SLOT (SEGA CD) %d...\n", load_state_slot+1);
+				char fname[1024];
+				emu_save_load_game(1, 0);
+				printf("LOADED FROM SLOT (SEGA CD) %d\n", load_state_slot+1);
+				load_state_slot = -1;
+			}
+			if(need_quick_load != -1){
+				load_state_file = quick_save_file;
+				need_quick_load = -1;
+			}
+			/* Load file */
+			if(load_state_file != NULL){
+				printf("LOADING FROM FILE (SEGA CD) %s...\n", load_state_file);
+				emu_save_load_game_from_file(1, load_state_file);
+				printf("LOADED FROM SLOT (SEGA CD) %s\n", load_state_file);
+				load_state_file = NULL;
+			}
+		}
+
+		frame_nb++;
+
+		pprof_end(main);
+	}
+
+	emu_set_fastforward(0);
+
+	// save SRAM
+	if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {
+		plat_status_msg_busy_first("Writing SRAM/BRAM...");
+		emu_save_load_game(0, 1);
+		Pico.sv.changed = 0;
+	}
+
+	pemu_loop_end();
+	emu_sound_stop();
+}

+ 2 - 1
platform/common/menu_pico.c

@@ -48,7 +48,7 @@ static const char *rom_exts[] = {
 	"bin", "smd", "gen", "md",
 	"iso", "cso", "cue", "chd",
 	"32x",
-	"sms",
+	"sms", "gg",
 	NULL
 };
 
@@ -1827,6 +1827,7 @@ static menu_entry e_menu_adv_options[] =
 	mee_onoff     ("Disable YM2612 SSG-EG",    MA_OPT2_DISABLE_YM_SSG,PicoIn.opt, POPT_DIS_FM_SSGEG),
 	mee_onoff     ("Emulate SN76496 (PSG)",    MA_OPT2_ENABLE_SN76496,PicoIn.opt, POPT_EN_PSG),
 	mee_onoff     ("Emulate YM2413 (FM)",      MA_OPT2_ENABLE_YM2413 ,PicoIn.opt, POPT_EN_YM2413),
+	mee_onoff     ("Emulate Game Gear LCD",    MA_OPT2_ENABLE_GGLCD  ,PicoIn.opt, POPT_EN_GG_LCD),
 	mee_onoff     ("Disable idle loop patching",MA_OPT2_NO_IDLE_LOOPS,PicoIn.opt, POPT_DIS_IDLE_DET),
 	mee_onoff     ("Disable frame limiter",    MA_OPT2_NO_FRAME_LIMIT,currentConfig.EmuOpt, EOPT_NO_FRMLIMIT),
 	mee_onoff     ("Enable dynarecs",          MA_OPT2_DYNARECS,      PicoIn.opt, POPT_EN_DRC),

+ 1 - 0
platform/common/menu_pico.h

@@ -115,6 +115,7 @@ typedef enum
 	MA_OPT2_DISABLE_YM_SSG,
 	MA_OPT2_ENABLE_SN76496,
 	MA_OPT2_ENABLE_YM2413,
+	MA_OPT2_ENABLE_GGLCD,
 	MA_OPT2_NO_LAST_ROM,
 	MA_OPT2_RAMTIMINGS,	/* gp2x */
 	MA_OPT2_NO_FRAME_LIMIT,	/* psp */

+ 30 - 5
platform/common/upscale.h

@@ -35,12 +35,20 @@
  */
 #include <pico/pico_types.h>
 
+
+/* LSB of all colors in a pixel */
+#if defined(USE_BGR555)
+#define PXLSB		0x0421
+#else
+#define PXLSB		0x0821
+#endif
+
 /* RGB565 pixel mixing, see https://www.compuphase.com/graphic/scale3.htm and
   			    http://blargg.8bitalley.com/info/rgb_mixing.html */
 /* 2-level mixing */
-//#define p_05(d,p1,p2)	d=(((p1)+(p2)  + ( ((p1)^(p2))&0x0821))>>1) // round up
-//#define p_05(d,p1,p2)	d=(((p1)+(p2)  - ( ((p1)^(p2))&0x0821))>>1) // round down
-#define p_05(d,p1,p2)	d=(((p1)&(p2)) + ((((p1)^(p2))&~0x0821)>>1))
+//#define p_05(d,p1,p2)	d=(((p1)+(p2)  + ( ((p1)^(p2))&PXLSB))>>1) // round up
+//#define p_05(d,p1,p2)	d=(((p1)+(p2)  - ( ((p1)^(p2))&PXLSB))>>1) // round down
+#define p_05(d,p1,p2)	d=(((p1)&(p2)) + ((((p1)^(p2))&~PXLSB)>>1))
 /* 4-level mixing, 2 times slower */
 // 1/4*p1 + 3/4*p2 = 1/2*(1/2*(p1+p2) + p2)
 #define p_025(d,p1,p2)	p_05(t, p1, p2); p_05( d, t, p2)
@@ -322,6 +330,21 @@ scalers h:
 	si += ss - w;					\
 } while (0)
 
+// reverse version for overlapping buffers
+#define rh_upscale_nn_1_2(di,ds,si,ss,w,f) do {	\
+	int i;						\
+	di += w*2;					\
+	si += w;					\
+	for (i = w/2; i > 0; i--, si -= 2, di -= 4) {	\
+		di[-1] = f(si[-1]);			\
+		di[-2] = f(si[-1]);			\
+		di[-3] = f(si[-2]);			\
+		di[-4] = f(si[-2]);			\
+	}						\
+	di += ds;					\
+	si += ss;					\
+} while (0)
+
 #define h_upscale_bl2_1_2(di,ds,si,ss,w,f) do {		\
 	int i; uint p = f(si[0]);			\
 	for (i = w/2; i > 0; i--, si += 2, di += 4) {	\
@@ -514,11 +537,12 @@ scalers v:
 	} else  {					\
 		int j;					\
 		l = 0;					\
-		di -= 4*ds;				\
+		di -= 3*ds;				\
 		for (j = 0; j < 2; j++) {		\
 			v_copy(&di[0], &di[-ds], w, f_nop); \
 			di += 2*ds;			\
 		}					\
+		di -= ds;			\
 	}						\
 } while (0)
 
@@ -528,11 +552,12 @@ scalers v:
 	} else  {					\
 		int j;					\
 		l = 0;					\
-		di -= 4*ds;				\
+		di -= 3*ds;				\
 		for (j = 0; j < 2; j++) {		\
 			v_mix(&di[0], &di[-ds], &di[ds], w, p_05, f_nop); \
 			di += 2*ds;			\
 		}					\
+		di -= ds;			\
 	}						\
 } while (0)
 

+ 22 - 25
platform/gp2x/emu.c

@@ -105,7 +105,7 @@ static void change_renderer(int diff)
 }
 
 #define is_16bit_mode() \
-	(currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X))
+	(currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X) || doing_bg_frame)
 
 static void (*osd_text)(int x, int y, const char *text);
 
@@ -248,7 +248,7 @@ static int EmuScanEnd8_rot(unsigned int num)
 
 /* line doublers */
 static unsigned int ld_counter;
-static int ld_left, ld_lines;
+static int ld_left, ld_lines; // numbers in Q1 format
 
 static int EmuScanBegin16_ld(unsigned int num)
 {
@@ -271,9 +271,9 @@ static int EmuScanEnd16_ld(unsigned int num)
 		emu_scan_end(ld_counter);
 
 	ld_counter++;
-	ld_left--;
+	ld_left -= 2;
 	if (ld_left <= 0) {
-		ld_left = ld_lines;
+		ld_left += ld_lines;
 
 		EmuScanBegin16_ld(num);
 		memcpy(Pico.est.DrawLineDest, oldline, 320 * gp2x_current_bpp / 8);
@@ -313,6 +313,7 @@ static int make_local_pal_md(int fast_mode)
 	else if (Pico.video.reg[0xC] & 8) { // shadow/hilight mode
 		bgr444_to_rgb32(localPal, Pico.est.SonicPal);
 		bgr444_to_rgb32_sh(localPal, Pico.est.SonicPal);
+		memcpy(localPal+0xc0, localPal, 0x40*4); // for spr prio mess
 	}
 	else {
 		bgr444_to_rgb32(localPal, Pico.est.SonicPal);
@@ -331,18 +332,7 @@ static int make_local_pal_md(int fast_mode)
 
 static int make_local_pal_sms(int fast_mode)
 {
-	unsigned short *spal = PicoMem.cram;
-	unsigned int *dpal = (void *)localPal;
-	unsigned int i, t;
-
-	for (i = 0x40; i > 0; i--) {
-		t = *spal++;
-		t = ((t & 0x0003) << 22) | ((t & 0x000c) << 12) | ((t & 0x0030) << 2);
-		t |= t >> 2;
-		t |= t >> 4;
-		*dpal++ = t;
-	}
-
+	bgr444_to_rgb32(localPal, PicoMem.cram);
 	Pico.m.dirtyPal = 0;
 	return 0x40;
 }
@@ -486,6 +476,9 @@ static void vid_reset_mode(void)
 	int gp2x_mode = 16;
 	int renderer = get_renderer();
 
+	if (doing_bg_frame)
+		renderer = RT_16BIT;
+
 	PicoIn.opt &= ~POPT_ALT_RENDERER;
 	emu_scan_begin = NULL;
 	emu_scan_end = NULL;
@@ -557,11 +550,13 @@ static void vid_reset_mode(void)
 
 	Pico.m.dirtyPal = 1;
 
-	PicoIn.opt &= ~POPT_EN_SOFTSCALE;
+	PicoIn.opt &= ~(POPT_DIS_32C_BORDER|POPT_EN_SOFTSCALE);
 	if (currentConfig.scaling == EOPT_SCALE_SW) {
 		PicoIn.opt |= POPT_EN_SOFTSCALE;
 		PicoIn.filter = EOPT_FILTER_BILINEAR2;
-	}
+	} else if (currentConfig.scaling == EOPT_SCALE_HW && is_16bit_mode())
+		// hw scaling, render without any padding
+		PicoIn.opt |= POPT_DIS_32C_BORDER;
 
 	// palette converters for 8bit modes
 	make_local_pal = (PicoIn.AHW & PAHW_SMS) ? make_local_pal_sms : make_local_pal_md;
@@ -572,6 +567,12 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 	int scalex = 320, scaley = 240;
 	int ln_offs = 0;
 
+	/* line doubling for swscaling, also needed for bg frames */
+	if (currentConfig.vscaling == EOPT_SCALE_SW && line_count < 240) {
+		ld_lines = ld_left = 2*line_count / (240 - line_count);
+		PicoDrawSetCallbacks(EmuScanBegin16_ld, EmuScanEnd16_ld);
+	}
+
 	if (doing_bg_frame)
 		return;
 
@@ -579,10 +580,8 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 	osd_y = 232;
 
 	/* set up hwscaling here */
-	PicoIn.opt &= ~POPT_DIS_32C_BORDER;
 	if (col_count < 320 && currentConfig.scaling == EOPT_SCALE_HW) {
 		scalex = col_count;
-		PicoIn.opt |= POPT_DIS_32C_BORDER;
 		osd_fps_x = col_count - (320-OSD_FPS_X);
 	}
 
@@ -594,11 +593,6 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 
 	gp2x_video_RGB_setscaling(ln_offs, scalex, scaley);
 
-	/* line doubling */
-	if (currentConfig.vscaling == EOPT_SCALE_SW && line_count < 240) {
-		ld_lines = ld_left = line_count / (240 - line_count);
-		PicoDrawSetCallbacks(EmuScanBegin16_ld, EmuScanEnd16_ld);
-	}
 
 	// clear whole screen in all buffers
 	if (!is_16bit_mode())
@@ -731,6 +725,8 @@ void pemu_forced_frame(int no_scale, int do_emu)
 	doing_bg_frame = 1;
 	PicoDrawSetCallbacks(NULL, NULL);
 	Pico.m.dirtyPal = 1;
+	PicoIn.opt &= ~POPT_DIS_32C_BORDER;
+	gp2x_current_bpp = 16;
 
 	if (!no_scale)
 		no_scale = currentConfig.scaling == EOPT_SCALE_NONE;
@@ -749,6 +745,7 @@ void plat_video_loop_prepare(void)
 	// make sure we are in correct mode
 	change_renderer(0);
 	vid_reset_mode();
+	rendstatus_old = -1;
 }
 
 void pemu_loop_prep(void)

+ 10 - 8
platform/libretro/libretro.c

@@ -89,8 +89,8 @@ static retro_audio_sample_batch_t audio_batch_cb;
 #define INITIAL_SND_RATE 44100
 
 static const float VOUT_PAR = 0.0;
-static const float VOUT_4_3 = (224.0f * (4.0f / 3.0f));
-static const float VOUT_CRT = (224.0f * 1.29911f);
+static const float VOUT_4_3 = (4.0f / 3.0f);
+static const float VOUT_CRT = (1.29911f);
 
 static bool show_overscan = false;
 static bool old_show_overscan = false;
@@ -746,7 +746,7 @@ void retro_get_system_info(struct retro_system_info *info)
 #define _GIT_VERSION "-" GIT_VERSION
 #endif
    info->library_version = VERSION _GIT_VERSION;
-   info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|chd|sms";
+   info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|chd|sms|gg";
    info->need_fullpath = true;
 }
 
@@ -899,7 +899,7 @@ typedef struct patch
 } patch;
 
 extern void decode(char *buff, patch *dest);
-extern uint16_t m68k_read16(uint32_t a);
+extern uint32_t m68k_read16(uint32_t a);
 extern void m68k_write16(uint32_t a, uint16_t d);
 
 void retro_cheat_reset(void)
@@ -1494,12 +1494,13 @@ static void update_variables(bool first_run)
    var.value = NULL;
    var.key = "picodrive_aspect";
    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
+      int height = vout_height >= 192 && vout_height <= 224 ? 224 : vout_height;
       if (strcmp(var.value, "4/3") == 0)
-         user_vout_width = VOUT_4_3;
+         user_vout_width = VOUT_4_3 * height;
       else if (strcmp(var.value, "CRT") == 0)
-         user_vout_width = VOUT_CRT;
+         user_vout_width = VOUT_CRT * height;
       else
-         user_vout_width = VOUT_PAR;
+         user_vout_width = VOUT_PAR * height;
    }
 
    if (user_vout_width != old_user_vout_width)
@@ -1801,7 +1802,8 @@ void retro_init(void)
    sceBlock = getVMBlock();
 #endif
 
-   PicoIn.opt = POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80|POPT_EN_YM2413
+   PicoIn.opt = POPT_EN_STEREO|POPT_EN_FM
+      | POPT_EN_PSG|POPT_EN_Z80|POPT_EN_YM2413|POPT_EN_GG_LCD
       | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX
       | POPT_EN_32X|POPT_EN_PWM
       | POPT_ACC_SPRITES|POPT_DIS_32C_BORDER;

+ 57 - 36
platform/linux/emu.c

@@ -95,7 +95,7 @@ static void draw_cd_leds(void)
  * for display isn't always possible.
  */
 
-static u16 *screen_buffer(u16 *buf)
+static inline u16 *screen_buffer(u16 *buf)
 {
 	return buf + screen_y * g_screen_ppitch + screen_x -
 			(out_y * g_screen_ppitch + out_x);
@@ -105,34 +105,46 @@ void screen_blit(u16 *pd, int pp, u8* ps, int ss, u16 *pal)
 {
 	typedef void (*upscale_t)
 			(u16 *di,int ds, u8 *si,int ss, int w,int h, u16 *pal);
-	upscale_t upscale_hv[] = {
+	static const upscale_t upscale_256_224_hv[] = {
 		upscale_rgb_nn_x_4_5_y_16_17,	upscale_rgb_snn_x_4_5_y_16_17,
 		upscale_rgb_bl2_x_4_5_y_16_17,	upscale_rgb_bl4_x_4_5_y_16_17,
 	};
-	upscale_t upscale_h[] = {
+	static const upscale_t upscale_256_224_h[] = {
 		upscale_rgb_nn_x_4_5,		upscale_rgb_snn_x_4_5,
 		upscale_rgb_bl2_x_4_5,		upscale_rgb_bl4_x_4_5,
 	};
-	upscale_t upscale_v[] = {
+	static const upscale_t upscale_256_224_v[] = {
 		upscale_rgb_nn_y_16_17,		upscale_rgb_snn_y_16_17,
 		upscale_rgb_bl2_y_16_17,	upscale_rgb_bl4_y_16_17,
 	};
-	upscale_t *upscale;
+	static const upscale_t upscale_160_144_hv[] = {
+		upscale_rgb_nn_x_1_2_y_3_5,	upscale_rgb_nn_x_1_2_y_3_5,
+		upscale_rgb_bl2_x_1_2_y_3_5,	upscale_rgb_bl4_x_1_2_y_3_5,
+	};
+	static const upscale_t upscale_160_144_h[] = {
+		upscale_rgb_nn_x_1_2,		upscale_rgb_nn_x_1_2,
+		upscale_rgb_bl2_x_1_2,		upscale_rgb_bl2_x_1_2,
+	};
+	static const upscale_t upscale_160_144_v[] = {
+		upscale_rgb_nn_y_3_5,		upscale_rgb_nn_y_3_5,
+		upscale_rgb_bl2_y_3_5,		upscale_rgb_bl4_y_3_5,
+	};
+	const upscale_t *upscale;
 	int y;
 
 	// handle software upscaling
 	upscale = NULL;
-	if (currentConfig.scaling == EOPT_SCALE_SW && out_w == 256) {
-		if (currentConfig.vscaling == EOPT_SCALE_SW && out_h <= 224)
-			// h+v scaling
-			upscale = upscale_hv;
-		else
-			// h scaling
-			upscale = upscale_h;
-	} else if (currentConfig.vscaling == EOPT_SCALE_SW && out_h <= 224) {
-			// v scaling
-			upscale = upscale_v;
-	} else {
+	if (currentConfig.scaling == EOPT_SCALE_SW) {
+	    if (currentConfig.vscaling == EOPT_SCALE_SW && out_h <= 224)
+		// h+v scaling
+		upscale = out_w == 256 ? upscale_256_224_hv: upscale_160_144_hv;
+	    else
+		// h scaling
+		upscale = out_w == 256 ? upscale_256_224_h : upscale_160_144_h;
+	} else if (currentConfig.vscaling == EOPT_SCALE_SW && out_h <= 224)
+		// v scaling
+		upscale = out_w == 256 ? upscale_256_224_v : upscale_160_144_v;
+	if (!upscale) {
 		// no scaling
 		for (y = 0; y < out_h; y++)
 			h_copy(pd, pp, ps, 328, out_w, f_pal);
@@ -286,16 +298,16 @@ static int vscale_state;
 
 static int cb_vscaling_begin(unsigned int line)
 {
-	static int prevline = 999;
-
 	// at start of new frame?
-	if (line < prevline) {
-		// set y frame offset (see emu_change_video_mode)
+	if (line <= out_y) {
+		// set y frame offset (see emu_video_mode_change)
 		Pico.est.DrawLineDest = screen_buffer(g_screen_ptr) +
-				(out_y * g_screen_ppitch + out_x);
+				(out_y * g_screen_ppitch /*+ out_x*/);
 		vscale_state = 0;
-	}
-	prevline = line;
+		return out_y - line;
+	} else if (line > out_y + out_h)
+		return 1;
+
 	return 0;
 }
 
@@ -307,16 +319,25 @@ static int cb_vscaling_nop(unsigned int line)
 static int cb_vscaling_end(unsigned int line)
 {
 	u16 *dest = Pico.est.DrawLineDest;
-	switch (currentConfig.filter) {
-	case 3: v_upscale_bl4_16_17(dest, g_screen_ppitch, 320, vscale_state);
-		break;
-	case 2: v_upscale_bl2_16_17(dest, g_screen_ppitch, 320, vscale_state);
-		break;
-	case 1: v_upscale_snn_16_17(dest, g_screen_ppitch, 320, vscale_state);
-		break;
-	default: v_upscale_nn_16_17(dest, g_screen_ppitch, 320, vscale_state);
-		break;
-	}
+
+	if (out_h == 144)
+	  switch (currentConfig.filter) {
+	  case 0: v_upscale_nn_3_5(dest, g_screen_ppitch, 320, vscale_state);
+		  break;
+	  default: v_upscale_snn_3_5(dest, g_screen_ppitch, 320, vscale_state);
+		  break;
+	  }
+	else
+	  switch (currentConfig.filter) {
+	  case 3: v_upscale_bl4_16_17(dest, g_screen_ppitch, 320, vscale_state);
+		  break;
+	  case 2: v_upscale_bl2_16_17(dest, g_screen_ppitch, 320, vscale_state);
+		  break;
+	  case 1: v_upscale_snn_16_17(dest, g_screen_ppitch, 320, vscale_state);
+		  break;
+	  default: v_upscale_nn_16_17(dest, g_screen_ppitch, 320, vscale_state);
+		  break;
+	  }
 	Pico.est.DrawLineDest = dest;
 	return 0;
 }
@@ -343,10 +364,10 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 	}
 	switch (currentConfig.vscaling) {
 	case EOPT_SCALE_HW:
-		screen_h = (out_h < 224 ? 224 : out_h);
+		screen_h = (out_h < 224 && out_h > 144 ? 224 : out_h);
 		screen_y = 0;
 		// NTSC always has 224 visible lines, anything smaller has bars
-		if (out_h < 224)
+		if (out_h < 224 && out_h > 144)
 			screen_y += (224 - out_h)/2;
 		// handle vertical centering for 16 bit mode
 		if (is_16bit_mode())
@@ -355,7 +376,7 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 	case EOPT_SCALE_SW:
 		screen_y = (screen_h - 240)/2;
 		// NTSC always has 224 visible lines, anything smaller has bars
-		if (out_h < 224)
+		if (out_h < 224 && out_h > 144)
 			screen_y += (224 - out_h)/2;
 		// in 16 bit mode sw scaling is divided between core and platform
 		if (is_16bit_mode() && out_h < 240)

+ 1 - 0
tools/mkoffsets.sh

@@ -136,6 +136,7 @@ get_define OFS_Pico_ Pico est			; echo "$line" >>$fn
 
 get_define OFS_PicoIn_ PicoInterface opt	; echo "$line" >>$fn
 get_define OFS_PicoIn_ PicoInterface filter	; echo "$line" >>$fn
+get_define OFS_PicoIn_ PicoInterface AHW	; echo "$line" >>$fn
 
 get_define OFS_EST_ PicoEState DrawScanline	; echo "$line" >>$fn
 get_define OFS_EST_ PicoEState rendstatus	; echo "$line" >>$fn