From 2433f409129279095926eb00cf8ab429738f80dd Mon Sep 17 00:00:00 2001
From: notaz <notasas@gmail.com>
Date: Sat, 31 Mar 2007 13:57:45 +0000
Subject: [PATCH] bugfixes, new scaling, double ym upd at 940

git-svn-id: file:///home/notaz/opt/svn/PicoDrive@83 be3aeb3a-fb24-0410-a615-afba39da0efa
---
 Pico/Cart.c                    |   2 -
 Pico/Pico.c                    |  10 ++-
 Pico/Pico.h                    |   2 +-
 Pico/PicoInt.h                 |   1 +
 Pico/Sek.c                     |   8 +++
 Pico/cd/Memory.c               |  86 ++++++++++++++---------
 Pico/cd/Memory.s               |  26 ++++++-
 Pico/sound/ym2612.c            |   7 ++
 platform/gp2x/940ctl.c         |  51 ++++++++++----
 platform/gp2x/Makefile         |   3 +-
 platform/gp2x/code940/940.c    |  75 ++++++++++++++------
 platform/gp2x/code940/Makefile |   5 +-
 platform/gp2x/emu.c            |  81 ++++++++++++++-------
 platform/gp2x/emu.h            |   4 +-
 platform/gp2x/gp2x.c           |  27 +++++--
 platform/gp2x/gp2x.h           |   2 +-
 platform/gp2x/menu.c           | 124 +++++++++++++++++++--------------
 platform/gp2x/version.h        |   2 +-
 platform/linux/Makefile        |   2 +-
 platform/linux/gp2x.c          |   2 +-
 platform/linux/port_config.h   |   2 +-
 platform/readme.txt            |   9 +++
 22 files changed, 360 insertions(+), 171 deletions(-)

diff --git a/Pico/Cart.c b/Pico/Cart.c
index 39546708..07ced8c5 100644
--- a/Pico/Cart.c
+++ b/Pico/Cart.c
@@ -79,10 +79,8 @@ zip_failed:
   f = fopen(path, "rb");
   if (f == NULL) return NULL;
 
-#ifndef NO_IONBF
   /* we use our own buffering */
   setvbuf(f, NULL, _IONBF, 0);
-#endif
 
   file = malloc(sizeof(*file));
   if (file == NULL) {
diff --git a/Pico/Pico.c b/Pico/Pico.c
index 4847f554..d0845e10 100644
--- a/Pico/Pico.c
+++ b/Pico/Pico.c
@@ -72,7 +72,7 @@ int PicoReset(int hard)
   PicoMemReset();
   SekReset();
   // s68k doesn't have the TAS quirk, so we just globally set normal TAS handler in MCD mode (used by Batman games).
-  CycloneSetRealTAS(PicoMCD & 1);
+  SekSetRealTAS(PicoMCD & 1);
   SekCycleCntT=0;
   z80_reset();
 
@@ -486,7 +486,13 @@ static int PicoFrameSimple(void)
   int y=0,line=0,lines=0,lines_step=0,sects;
   int cycles_68k_vblock,cycles_68k_block;
 
-  if(Pico.m.pal) {
+  // we don't emulate DMA timing in this mode
+  if (Pico.m.dma_bytes) {
+    Pico.m.dma_bytes=0;
+    Pico.video.status&=~2;
+  }
+
+  if (Pico.m.pal) {
     // M68k cycles/frame: 152009.78
     if(pv->reg[1]&8) { // 240 lines
       cycles_68k_block  = (int) ((double) OSC_PAL  /  7 / 50 / 312 * 15 + 0.4); // 16 sects, 16*15=240, 7308
diff --git a/Pico/Pico.h b/Pico/Pico.h
index 597f0211..a8b276de 100644
--- a/Pico/Pico.h
+++ b/Pico/Pico.h
@@ -31,7 +31,7 @@ void mp3_update(int *buffer, int length, int stereo);
 // enable_ym2612&dac, enable_sn76496, enable_z80, stereo_sound,
 // alt_renderer, 6button_gamepad, accurate_timing, accurate_sprites,
 // draw_no_32col_border, external_ym2612, enable_pcm, enable cdda
-// enable_cdgfx, cd_perfect_sync
+// enable_cdgfx, cd_perfect_sync, soft_32col_scaling
 extern int PicoOpt;
 extern int PicoVer;
 extern int PicoSkipFrame; // skip rendering frame, but still do sound (if enabled) and emulation stuff
diff --git a/Pico/PicoInt.h b/Pico/PicoInt.h
index 5c71f8a7..e11f58bc 100644
--- a/Pico/PicoInt.h
+++ b/Pico/PicoInt.h
@@ -311,6 +311,7 @@ int SekInit(void);
 int SekReset(void);
 int SekInterrupt(int irq);
 void SekState(unsigned char *data);
+void SekSetRealTAS(int use_real);
 
 // cd/Sek.c
 int SekInitS68k(void);
diff --git a/Pico/Sek.c b/Pico/Sek.c
index 0ca575f8..e05f6f4e 100644
--- a/Pico/Sek.c
+++ b/Pico/Sek.c
@@ -186,3 +186,11 @@ void SekState(unsigned char *data)
   memcpy(data+0x40,&PicoM68kCPU.pc, 0x04);
 #endif
 }
+
+void SekSetRealTAS(int use_real)
+{
+#ifdef EMU_C68K
+  CycloneSetRealTAS(use_real);
+#endif
+}
+
diff --git a/Pico/cd/Memory.c b/Pico/cd/Memory.c
index 08a4486f..fc6e4de5 100644
--- a/Pico/cd/Memory.c
+++ b/Pico/cd/Memory.c
@@ -30,10 +30,13 @@ typedef unsigned int   u32;
 #define rdprintf(...)
 //#define wrdprintf dprintf
 #define wrdprintf(...)
+//#define plprintf dprintf
+#define plprintf(...)
 
 // -----------------------------------------------------------------
 
 // poller detection
+//#undef USE_POLL_DETECT
 #define POLL_LIMIT 16
 #define POLL_CYCLES 124
 // int m68k_poll_addr, m68k_poll_cnt;
@@ -143,7 +146,7 @@ void m68k_reg_write8(u32 a, u32 d)
 #ifdef USE_POLL_DETECT
       if ((s68k_poll_adclk&0xfe) == 2 && s68k_poll_cnt > POLL_LIMIT) {
         SekSetStopS68k(0); s68k_poll_adclk = 0;
-        //printf("%05i:%03i: s68k poll release, a=%02x\n", Pico.m.frame_count, Pico.m.scanline, a);
+        plprintf("s68k poll release, a=%02x\n", a);
       }
 #endif
       return;
@@ -163,7 +166,7 @@ void m68k_reg_write8(u32 a, u32 d)
 #ifdef USE_POLL_DETECT
       if ((s68k_poll_adclk&0xfe) == 0xe && s68k_poll_cnt > POLL_LIMIT) {
         SekSetStopS68k(0); s68k_poll_adclk = 0;
-        //printf("%05i:%03i: s68k poll release, a=%02x\n", Pico.m.frame_count, Pico.m.scanline, a);
+        plprintf("s68k poll release, a=%02x\n", a);
       }
 #endif
       return;
@@ -174,7 +177,7 @@ void m68k_reg_write8(u32 a, u32 d)
 #ifdef USE_POLL_DETECT
       if ((a&0xfe) == (s68k_poll_adclk&0xfe) && s68k_poll_cnt > POLL_LIMIT) {
         SekSetStopS68k(0); s68k_poll_adclk = 0;
-        //printf("%05i:%03i: s68k poll release, a=%02x\n", Pico.m.frame_count, Pico.m.scanline, a);
+        plprintf("s68k poll release, a=%02x\n", a);
       }
 #endif
       return;
@@ -183,6 +186,31 @@ void m68k_reg_write8(u32 a, u32 d)
   dprintf("m68k FIXME: invalid write? [%02x] %02x", a, d);
 }
 
+#ifndef _ASM_CD_MEMORY_C
+static
+#endif
+u32 s68k_poll_detect(u32 a, u32 d)
+{
+#ifdef USE_POLL_DETECT
+  // polling detection
+  if (a == (s68k_poll_adclk&0xff)) {
+    unsigned int clkdiff = SekCyclesDoneS68k() - (s68k_poll_adclk>>8);
+    if (clkdiff <= POLL_CYCLES) {
+      s68k_poll_cnt++;
+      //printf("-- diff: %u, cnt = %i\n", clkdiff, s68k_poll_cnt);
+      if (s68k_poll_cnt > POLL_LIMIT) {
+        SekSetStopS68k(1);
+        plprintf("s68k poll detected @ %06x, a=%02x\n", SekPcS68k, a);
+      }
+      s68k_poll_adclk = (SekCyclesDoneS68k() << 8) | a;
+      return d;
+    }
+  }
+  s68k_poll_adclk = (SekCyclesDoneS68k() << 8) | a;
+  s68k_poll_cnt = 0;
+#endif
+  return d;
+}
 
 #define READ_FONT_DATA(basemask) \
 { \
@@ -208,9 +236,9 @@ u32 s68k_reg_read16(u32 a)
     case 0:
       return ((Pico_mcd->s68k_regs[0]&3)<<8) | 1; // ver = 0, not in reset state
     case 2:
-      d = (Pico_mcd->s68k_regs[a]<<8) | (Pico_mcd->s68k_regs[a+1]&0x1f);
+      d = (Pico_mcd->s68k_regs[2]<<8) | (Pico_mcd->s68k_regs[3]&0x1f);
       //printf("s68k_regs r3: %02x @%06x\n", (u8)d, SekPcS68k);
-      goto poll_detect;
+      return s68k_poll_detect(a, d);
     case 6:
       return CDC_Read_Reg();
     case 8:
@@ -240,30 +268,9 @@ u32 s68k_reg_read16(u32 a)
 
   d = (Pico_mcd->s68k_regs[a]<<8) | Pico_mcd->s68k_regs[a+1];
 
-  if (a >= 0x0e && a < 0x30) goto poll_detect;
-
-  return d;
-
-poll_detect:
-#ifdef USE_POLL_DETECT
-  // polling detection
-  if (a == (s68k_poll_adclk&0xfe)) {
-    unsigned int clkdiff = SekCyclesDoneS68k() - (s68k_poll_adclk>>8);
-    if (clkdiff <= POLL_CYCLES) {
-      s68k_poll_cnt++;
-      //printf("-- diff: %u, cnt = %i\n", clkdiff, s68k_poll_cnt);
-      if (s68k_poll_cnt > POLL_LIMIT) {
-        SekSetStopS68k(1);
-        //printf("%05i:%03i: s68k poll detected @ %06x, a=%02x\n", Pico.m.frame_count, Pico.m.scanline, SekPcS68k, a);
-      }
-      s68k_poll_adclk = (SekCyclesDoneS68k() << 8) | a;
-      return d;
-    }
-  }
-  s68k_poll_adclk = (SekCyclesDoneS68k() << 8) | a;
-  s68k_poll_cnt = 0;
+  if (a >= 0x0e && a < 0x30)
+    return s68k_poll_detect(a, d);
 
-#endif
   return d;
 }
 
@@ -407,6 +414,7 @@ static void OtherWrite8End(u32 a, u32 d, int realsize)
 #include "cell_map.c"
 #endif // !def _ASM_CD_MEMORY_C
 
+
 // -----------------------------------------------------------------
 //                     Read Rom and read Ram
 
@@ -631,8 +639,11 @@ static void PicoWriteM68k8(u32 a,u8 d)
     return;
   }
 
-  if ((a&0xffffc0)==0xa12000)
+  if ((a&0xffffc0)==0xa12000) {
     rdprintf("m68k_regs w8: [%02x] %02x @%06x", a&0x3f, d, SekPc);
+    m68k_reg_write8(a, d);
+    return;
+  }
 
   OtherWrite8(a,d,8);
 }
@@ -688,7 +699,7 @@ static void PicoWriteM68k16(u32 a,u16 d)
 #ifdef USE_POLL_DETECT
       if ((s68k_poll_adclk&0xfe) == 0xe && s68k_poll_cnt > POLL_LIMIT) {
         SekSetStopS68k(0); s68k_poll_adclk = -1;
-        //printf("%05i:%03i: s68k poll release, a=%02x\n", Pico.m.frame_count, Pico.m.scanline, a);
+        plprintf("s68k poll release, a=%02x\n", a);
       }
 #endif
       return;
@@ -755,8 +766,10 @@ static void PicoWriteM68k32(u32 a,u32 d)
     return;
   }
 
-  if ((a&0xffffc0)==0xa12000)
+  if ((a&0xffffc0)==0xa12000) {
     rdprintf("m68k_regs w32: [%02x] %08x @%06x", a&0x3f, d, SekPc);
+    if ((a&0x3e) == 0xe) dprintf("m68k FIXME: w32 [%02x]", a&0x3f);
+  }
 
   OtherWrite16(a,  (u16)(d>>16));
   OtherWrite16(a+2,(u16)d);
@@ -785,7 +798,13 @@ static u8 PicoReadS68k8(u32 a)
   if ((a&0xfffe00) == 0xff8000) {
     a &= 0x1ff;
     rdprintf("s68k_regs r8: [%02x] @ %06x", a, SekPcS68k);
-    if (a >= 0x58 && a < 0x68)
+    if (a >= 0x0e && a < 0x30) {
+      d = Pico_mcd->s68k_regs[a];
+      s68k_poll_detect(a, d);
+      rdprintf("ret = %02x", (u8)d);
+      goto end;
+    }
+    else if (a >= 0x58 && a < 0x68)
          d = gfx_cd_read(a&~1);
     else d = s68k_reg_read16(a&~1);
     if ((a&1)==0) d>>=8;
@@ -1008,7 +1027,7 @@ static u32 PicoReadS68k32(u32 a)
 
   // PCM
   if ((a&0xff8000)==0xff0000) {
-    dprintf("FIXME: s68k_pcm r32: [%06x] @%06x", a, SekPcS68k);
+    dprintf("s68k_pcm r32: [%06x] @%06x", a, SekPcS68k);
     a &= 0x7fff;
     if (a >= 0x2000) {
       a >>= 1;
@@ -1293,6 +1312,7 @@ static void PicoWriteS68k32(u32 a,u32 d)
       gfx_cd_write16(a,   d>>16);
       gfx_cd_write16(a+2, d&0xffff);
     } else {
+      if ((a&0x1fe) == 0xe) dprintf("s68k FIXME: w32 [%02x]", a&0x3f);
       s68k_reg_write8(a,   d>>24);
       s68k_reg_write8(a+1,(d>>16)&0xff);
       s68k_reg_write8(a+2,(d>>8) &0xff);
diff --git a/Pico/cd/Memory.s b/Pico/cd/Memory.s
index d10f1a51..33286678 100644
--- a/Pico/cd/Memory.s
+++ b/Pico/cd/Memory.s
@@ -135,6 +135,8 @@ m_s68k_decode_write_table:
 .extern s68k_reg_write8
 .extern s68k_poll_adclk
 .extern PicoCpuS68k
+.extern s68k_poll_detect
+.extern SN76496Write
 
 
 @ r0=reg3, r1-r3=temp
@@ -993,6 +995,10 @@ m_m68k_write8_vdp:
     tst     r0, #0x70000
     tsteq   r0, #0x000e0
     bxne    lr                          @ invalid
+    and     r2, r0, #0x19
+    cmp     r2, #0x11
+    andeq   r0, r1, #0xff
+    beq     SN76496Write
     and     r1, r1, #0xff
     orr     r1, r1, r1, lsl #8          @ byte access gets mirrored
     b       PicoVideoWrite
@@ -1123,7 +1129,11 @@ m_m68k_write16_vdp:
     tsteq   r0, #0x000e0
     bxne    lr              @ invalid
     bic     r0, r0, #1
-    b       PicoVideoWrite
+    and     r2, r0, #0x18
+    cmp     r2, #0x10
+    bne     PicoVideoWrite
+    and     r0, r1, #0xff
+    b       SN76496Write    @ lsb goes to 0x11
 
 
 m_m68k_write16_ram:
@@ -1291,6 +1301,10 @@ m_m68k_write32_vdp:
     tst     r0, #0x70000
     tsteq   r0, #0x000e0
     bxne    lr              @ invalid
+    and     r2, r0, #0x18
+    cmp     r2, #0x10
+    moveq   r0, r1, lsr #16
+    beq     SN76496Write    @ which game is crazy enough to do that?
     stmfd   sp!,{r0,r1,lr}
     mov     r1, r1, lsr #16
     bl      PicoVideoWrite
@@ -1410,6 +1424,9 @@ m_s68k_read8_regs:
     tst     r0, #0x7e00
     movne   r0, #0
     bxne    lr
+    sub     r2, r0, #0x0e
+    cmp     r2, #(0x30-0x0e)
+    blo     m_s68k_read8_comm
     sub     r2, r0, #0x58
     cmp     r2, #0x10
     ldrlo   r2, =gfx_cd_read
@@ -1424,6 +1441,13 @@ m_s68k_read8_regs:
     and     r0, r0, #0xff
     bx      lr
 
+m_s68k_read8_comm:
+    ldr     r1, =(Pico+0x22200)
+    ldr     r1, [r1]
+    add     r1, r1, #0x110000
+    ldrb    r1, [r1, r0]
+    b       s68k_poll_detect
+
 
 @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
diff --git a/Pico/sound/ym2612.c b/Pico/sound/ym2612.c
index 3c3b17e6..754f4969 100644
--- a/Pico/sound/ym2612.c
+++ b/Pico/sound/ym2612.c
@@ -1702,7 +1702,9 @@ int YM2612Write_(unsigned int a, unsigned int v)
 		}
 
 		addr = ym2612.OPN.ST.address;
+#ifndef EXTERNAL_YM2612
 		ym2612.REGS[addr] = v;
+#endif
 
 		switch( addr & 0xf0 )
 		{
@@ -1800,7 +1802,9 @@ int YM2612Write_(unsigned int a, unsigned int v)
 		}
 
 		addr = ym2612.OPN.ST.address | 0x100;
+#ifndef EXTERNAL_YM2612
 		ym2612.REGS[addr] = v;
+#endif
 
 		ret = OPNWriteReg(addr, v);
 		break;
@@ -1869,7 +1873,10 @@ void YM2612PicoStateLoad_(void)
 }
 
 
+#ifndef EXTERNAL_YM2612
 void *YM2612GetRegs(void)
 {
 	return ym2612.REGS;
 }
+#endif
+
diff --git a/platform/gp2x/940ctl.c b/platform/gp2x/940ctl.c
index bcbb09b8..f2e98a99 100644
--- a/platform/gp2x/940ctl.c
+++ b/platform/gp2x/940ctl.c
@@ -66,8 +66,10 @@ static int   writebuff_ptr = 0;
 
 
 /* OPN Mode Register Write */
-static void set_timers( int v )
+static int set_timers( int v )
 {
+	int change;
+
 	/* b7 = CSM MODE */
 	/* b6 = 3 slot mode */
 	/* b5 = reset b */
@@ -76,6 +78,7 @@ static void set_timers( int v )
 	/* b2 = timer enable a */
 	/* b1 = load b */
 	/* b0 = load a */
+	change = (ST_mode ^ v) & 0xc0;
 	ST_mode = v;
 
 	/* reset Timer b flag */
@@ -85,6 +88,8 @@ static void set_timers( int v )
 	/* reset Timer a flag */
 	if( v & 0x10 )
 		ST_status &= ~1;
+
+	return change;
 }
 
 /* YM2612 write */
@@ -93,11 +98,14 @@ static void set_timers( int v )
 /* returns 1 if sample affecting state changed */
 int YM2612Write_940(unsigned int a, unsigned int v)
 {
-	int addr; //, ret=1;
+	int addr;
+	int upd = 1;	/* the write affects sample generation */
 
 	v &= 0xff;	/* adjust to 8 bit bus */
 	a &= 3;
 
+	//printf("%05i:%03i: ym w ([%i] %02x)\n", Pico.m.frame_count, Pico.m.scanline, a, v);
+
 	switch( a ) {
 	case 0:	/* address port 0 */
 		if (!addr_A1 && ST_address == v)
@@ -108,7 +116,7 @@ int YM2612Write_940(unsigned int a, unsigned int v)
 			(v == 0x24 || v == 0x25 || v == 0x26 || v == 0x2a))
 				return 0;
 		addr_A1 = 0;
-		//ret=0;
+		upd = 0;
 		break;
 
 	case 1:	/* data port 0    */
@@ -152,14 +160,16 @@ int YM2612Write_940(unsigned int a, unsigned int v)
 				}
 				return 0;
 			case 0x27:	/* mode, timer control */
-				set_timers( v );
-				break; // other side needs ST.mode for 3slot mode
+				if (set_timers( v ))
+					break; // other side needs ST.mode for 3slot mode
+				return 0;
 			case 0x2a:	/* DAC data (YM2612) */
 				dacout = ((int)v - 0x80) << 6;	/* level unknown (notaz: 8 seems to be too much) */
 				return 0;
 			case 0x2b:	/* DAC Sel  (YM2612) */
 				/* b7 = dac enable */
 				dacen = v & 0x80;
+				upd = 0;
 				break; // other side has to know this
 			default:
 				break;
@@ -173,7 +183,7 @@ int YM2612Write_940(unsigned int a, unsigned int v)
 			return 0;
 		ST_address = v;
 		addr_A1 = 1;
-		//ret=0;
+		upd = 0;
 		break;
 
 	case 3:	/* data port 1    */
@@ -186,14 +196,25 @@ int YM2612Write_940(unsigned int a, unsigned int v)
 		break;
 	}
 
+	//printf("ym pass\n");
+
 	if(currentConfig.EmuOpt & 4) {
-		/* queue this write for 940 */
-		if (writebuff_ptr < 2047) {
-			if (shared_ctl->writebuffsel == 1) {
-				shared_ctl->writebuff0[writebuff_ptr++] = (a<<8)|v;
-			} else {
-				shared_ctl->writebuff1[writebuff_ptr++] = (a<<8)|v;
+		UINT16 *writebuff = shared_ctl->writebuffsel ? shared_ctl->writebuff0 : shared_ctl->writebuff1;
+
+		/* detect rapid ym updates */
+		if (upd && !(writebuff_ptr & 0x80000000) && Pico.m.scanline < 224) {
+			int mid = Pico.m.pal ? 68 : 93;
+			if (Pico.m.scanline > mid) {
+				//printf("%05i:%03i: rapid ym\n", Pico.m.frame_count, Pico.m.scanline);
+				writebuff[writebuff_ptr++ & 0xffff] = 0xfffe;
+				writebuff_ptr |= 0x80000000;
+				//printf("%05i:%03i: ym w ([%02x] %02x, upd=%i)\n", Pico.m.frame_count, Pico.m.scanline, addr, v, upd);
 			}
+		}
+
+		/* queue this write for 940 */
+		if ((writebuff_ptr&0xffff) < 2047) {
+			writebuff[writebuff_ptr++ & 0xffff] = (a<<8)|v;
 		} else {
 			printf("warning: writebuff_ptr > 2047 ([%i] %02x)\n", a, v);
 		}
@@ -301,7 +322,7 @@ void YM2612PicoStateLoad_940(void)
 
 	addr_A1 = old_A1;
 
-	add_job_940(JOB940_PICOSTATELOAD);
+//	add_job_940(JOB940_PICOSTATELOAD);
 }
 
 
@@ -465,9 +486,9 @@ int YM2612UpdateOne_940(int *buffer, int length, int stereo, int is_buf_empty)
 	else memset32(buffer, 0, length<<stereo);
 
 	if (shared_ctl->writebuffsel == 1) {
-		shared_ctl->writebuff0[writebuff_ptr] = 0xffff;
+		shared_ctl->writebuff0[writebuff_ptr & 0xffff] = 0xffff;
 	} else {
-		shared_ctl->writebuff1[writebuff_ptr] = 0xffff;
+		shared_ctl->writebuff1[writebuff_ptr & 0xffff] = 0xffff;
 	}
 	writebuff_ptr = 0;
 
diff --git a/platform/gp2x/Makefile b/platform/gp2x/Makefile
index b045c2d9..07f99924 100644
--- a/platform/gp2x/Makefile
+++ b/platform/gp2x/Makefile
@@ -84,7 +84,8 @@ OBJS += mp3.o
 # CPU cores
 ifeq "$(use_musashi)" "1"
 DEFINC += -DEMU_M68K
-OBJS += _build\m68kcpu.o _build\m68kopac.o _build\m68kopdm.o _build\m68kopnz.o _build\m68kops.o
+OBJS += ../../cpu/musashi/m68kcpu.o ../../cpu/musashi/m68kopac.o ../../cpu/musashi/m68kopdm.o
+OBJS += ../../cpu/musashi/m68kopnz.o ../../cpu/musashi/m68kops.o
 else
 DEFINC += -DEMU_C68K
 OBJS += ../../cpu/Cyclone/proj/Cyclone.o
diff --git a/platform/gp2x/code940/940.c b/platform/gp2x/code940/940.c
index c37c80e0..4a1e718e 100644
--- a/platform/gp2x/code940/940.c
+++ b/platform/gp2x/code940/940.c
@@ -55,6 +55,56 @@ static void mp3_decode(void)
 	set_if_not_changed(&shared_ctl->mp3_offs, mp3_offs, readPtr - mp3_data);
 }
 
+static void ym_update(int *ym_buffer)
+{
+	int i, dw;
+	int two_upds = 0;
+	UINT16 *wbuff;
+
+	if (shared_ctl->writebuffsel == 1) {
+		wbuff = shared_ctl->writebuff1;
+	} else {
+		wbuff = shared_ctl->writebuff0;
+	}
+
+	/* playback all writes */
+	for (i = 2048/2; i > 0; i--) {
+		UINT16 d;
+		dw = *(int *)wbuff;
+		d = dw;
+		wbuff++;
+		if (d == 0xffff) break;
+		if (d == 0xfffe) { two_upds=1; break; }
+		YM2612Write_(d >> 8, d);
+		d = (dw>>16);
+		wbuff++;
+		if (d == 0xffff) break;
+		if (d == 0xfffe) { two_upds=1; break; }
+		YM2612Write_(d >> 8, d);
+	}
+
+	if (two_upds) {
+		int len1 = shared_ctl->length / 2;
+		shared_ctl->ym_active_chs =
+			YM2612UpdateOne_(ym_buffer, len1, shared_ctl->stereo, 1);
+
+		for (i *= 2; i > 0; i--) {
+			UINT16 d = *wbuff++;
+			if (d == 0xffff) break;
+			YM2612Write_(d >> 8, d);
+		}
+
+		ym_buffer += shared_ctl->stereo ? len1*2 : len1;
+		len1 = shared_ctl->length - len1;
+
+		shared_ctl->ym_active_chs =
+			YM2612UpdateOne_(ym_buffer, len1, shared_ctl->stereo, 1);
+	} else {
+		shared_ctl->ym_active_chs =
+			YM2612UpdateOne_(ym_buffer, shared_ctl->length, shared_ctl->stereo, 1);
+	}
+}
+
 
 void Main940(void)
 {
@@ -92,30 +142,9 @@ void Main940(void)
 				YM2612PicoStateLoad_();
 				break;
 
-			case JOB940_YM2612UPDATEONE: {
-				int i, dw, *wbuff;
-				if (shared_ctl->writebuffsel == 1) {
-					wbuff = (int *) shared_ctl->writebuff1;
-				} else {
-					wbuff = (int *) shared_ctl->writebuff0;
-				}
-
-				/* playback all writes */
-				for (i = 2048/2; i > 0; i--) {
-					UINT16 d;
-					dw = *wbuff++;
-					d = dw;
-					if (d == 0xffff) break;
-					YM2612Write_(d >> 8, d);
-					d = (dw>>16);
-					if (d == 0xffff) break;
-					YM2612Write_(d >> 8, d);
-				}
-
-				shared_ctl->ym_active_chs =
-					YM2612UpdateOne_(ym_buffer, shared_ctl->length, shared_ctl->stereo, 1);
+			case JOB940_YM2612UPDATEONE:
+				ym_update(ym_buffer);
 				break;
-			}
 
 			case JOB940_MP3DECODE:
 				mp3_decode();
diff --git a/platform/gp2x/code940/Makefile b/platform/gp2x/code940/Makefile
index 58583812..0af15b9e 100644
--- a/platform/gp2x/code940/Makefile
+++ b/platform/gp2x/code940/Makefile
@@ -32,7 +32,7 @@ all: code940.bin
 # stuff for 940 core
 
 # init, emu_control, emu
-OBJS940 += 940init.o 940.o 940ym2612.o memcpy.o mix.o
+OBJS940 += 940init.o 940.o 940ym2612.o memcpy.o mix.o misc.o
 # the asm code seems to be faster when run on 920, but not on 940 for some reason
 # OBJS940 += ../../Pico/sound/ym2612_asm.o
 
@@ -56,6 +56,9 @@ code940.gpe : $(OBJS940) ../helix/helix_mp3.a
 mix.o : ../../../Pico/sound/mix.s
 	@echo $@
 	@$(GCC) $(COPT) $(DEFINC) -DEXTERNAL_YM2612 -c $< -o $@
+misc.o : ../../../Pico/misc.s
+	@echo $@
+	@$(GCC) $(COPT) $(DEFINC) -DEXTERNAL_YM2612 -c $< -o $@
 
 ../helix/helix_mp3.a:
 	@make -C ../helix/
diff --git a/platform/gp2x/emu.c b/platform/gp2x/emu.c
index f43f9b72..99357b25 100644
--- a/platform/gp2x/emu.c
+++ b/platform/gp2x/emu.c
@@ -63,21 +63,7 @@ unsigned char *framebuff = 0;  // temporary buffer for alt renderer
 int state_slot = 0;
 int reset_timing = 0;
 
-/*
-// tmp
-static FILE *logf = NULL;
 
-void pprintf(char *texto, ...)
-{
-	va_list args;
-
-	va_start(args,texto);
-	vfprintf(logf,texto,args);
-	va_end(args);
-	fflush(logf);
-	sync();
-}
-*/
 // utilities
 static void strlwr(char* string)
 {
@@ -467,6 +453,18 @@ static void find_combos(void)
 }
 
 
+void scaling_update(void)
+{
+	PicoOpt &= ~0x4100;
+	switch (currentConfig.scaling) {
+		default: break; // off
+		case 1:  // hw hor
+		case 2:  PicoOpt |=  0x0100; break; // hw hor+vert
+		case 3:  PicoOpt |=  0x4000; break; // sw hor
+	}
+}
+
+
 int emu_ReadConfig(int game)
 {
 	FILE *f;
@@ -501,6 +499,7 @@ int emu_ReadConfig(int game)
 		currentConfig.KeyBinds[22] = 1<<30; // vol down
 		currentConfig.gamma = 100;
 		currentConfig.PicoCDBuffers = 64;
+		currentConfig.scaling = 0;
 		strncpy(cfg, PicoConfigFile, 511);
 		cfg[511] = 0;
 	} else {
@@ -516,7 +515,7 @@ int emu_ReadConfig(int game)
 		bread = fread(&currentConfig, 1, sizeof(currentConfig), f);
 		fclose(f);
 	}
-	printf((bread == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n");
+	printf(bread > 0 ? "(ok)\n" : "(failed)\n");
 
 	PicoOpt = currentConfig.PicoOpt;
 	PsndRate = currentConfig.PsndRate;
@@ -527,6 +526,7 @@ int emu_ReadConfig(int game)
 		actionNames[ 8] = "Z"; actionNames[ 9] = "Y";
 		actionNames[10] = "X"; actionNames[11] = "MODE";
 	}
+	scaling_update();
 	// some sanity checks
 	if (currentConfig.CPUclock < 1 || currentConfig.CPUclock > 4096) currentConfig.CPUclock = 200;
 	if (currentConfig.gamma < 10 || currentConfig.gamma > 300) currentConfig.gamma = 100;
@@ -536,7 +536,7 @@ int emu_ReadConfig(int game)
 		currentConfig.KeyBinds[22] = 1<<30; // vol down
 	}
 
-	return (bread == sizeof(currentConfig));
+	return (bread > 0); // == sizeof(currentConfig));
 }
 
 
@@ -565,7 +565,9 @@ int emu_WriteConfig(int game)
 		bwrite = fwrite(&currentConfig, 1, sizeof(currentConfig), f);
 		fflush(f);
 		fclose(f);
+#ifndef NO_SYNC
 		sync();
+#endif
 	}
 	printf((bwrite == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n");
 
@@ -581,8 +583,23 @@ void emu_Deinit(void)
 		SRam.changed = 0;
 	}
 
-	if (!(currentConfig.EmuOpt & 0x20))
-		emu_WriteConfig(0);
+	if (!(currentConfig.EmuOpt & 0x20)) {
+		FILE *f = fopen(PicoConfigFile, "r+b");
+		if (!f) emu_WriteConfig(0);
+		else {
+			// if we already have config, reload it, except last ROM
+			fseek(f, sizeof(currentConfig.lastRomFile), SEEK_SET);
+			fread(&currentConfig.EmuOpt, 1, sizeof(currentConfig) - sizeof(currentConfig.lastRomFile), f);
+			fseek(f, 0, SEEK_SET);
+			fwrite(&currentConfig, 1, sizeof(currentConfig), f);
+			fflush(f);
+			fclose(f);
+#ifndef NO_SYNC
+			sync();
+#endif
+		}
+	}
+
 	free(framebuff);
 
 	PicoExit();
@@ -706,9 +723,13 @@ static void blit(const char *fps, const char *notice)
 		}
 	}
 
-	if (notice) osd_text(4, 232, notice);
-	if (emu_opt & 2)
-		osd_text(osd_fps_x, 232, fps);
+	if (notice || (emu_opt & 2)) {
+		int h = 232;
+		if (currentConfig.scaling == 2 && !(Pico.video.reg[1]&8)) h -= 8;
+		if (notice) osd_text(4, h, notice);
+		if (emu_opt & 2)
+			osd_text(osd_fps_x, h, fps);
+	}
 	if ((emu_opt & 0x400) && (PicoMCD & 1))
 		cd_leds();
 
@@ -771,7 +792,9 @@ static void vidResetMode(void)
 	}
 	Pico.m.dirtyPal = 1;
 	// reset scaling
-	gp2x_video_RGB_setscaling((PicoOpt&0x100)&&!(Pico.video.reg[12]&1) ? 256 : 320, 240);
+	if (currentConfig.scaling == 2 && !(Pico.video.reg[1]&8))
+	     gp2x_video_RGB_setscaling(8, (PicoOpt&0x100)&&!(Pico.video.reg[12]&1) ? 256 : 320, 224);
+	else gp2x_video_RGB_setscaling(0, (PicoOpt&0x100)&&!(Pico.video.reg[12]&1) ? 256 : 320, 240);
 }
 
 
@@ -1133,7 +1156,9 @@ void emu_Loop(void)
 					vidCpyM2 = vidCpyM2_32col;
 				}
 			}
-			gp2x_video_RGB_setscaling(scalex, 240);
+			if (currentConfig.scaling == 2 && !(modes&8)) // want vertical scaling and game is not in 240 line mode
+			     gp2x_video_RGB_setscaling(8, scalex, 224);
+			else gp2x_video_RGB_setscaling(0, scalex, 240);
 			oldmodes = modes;
 			clearArea(1);
 		}
@@ -1171,7 +1196,7 @@ void emu_Loop(void)
 				if (frames_shown > frames_done) frames_shown = frames_done;
 			}
 		}
-#if 0
+#if 1
 		sprintf(fpsbuff, "%05i", Pico.m.frame_count);
 #endif
 		lim_time = (frames_done+1) * target_frametime;
@@ -1446,7 +1471,9 @@ int emu_SaveLoadGame(int load, int sram)
 				ret = fwrite(sram_data, 1, sram_size, sramFile);
 				ret = (ret != sram_size) ? -1 : 0;
 				fclose(sramFile);
+#ifndef NO_SYNC
 				sync();
+#endif
 			}
 		}
 		return ret;
@@ -1470,8 +1497,10 @@ int emu_SaveLoadGame(int load, int sram)
 			ret = PmovState(load ? 6 : 5, PmovFile);
 			areaClose(PmovFile);
 			PmovFile = 0;
-			if (!load) sync();
-			else Pico.m.dirtyPal=1;
+			if (load) Pico.m.dirtyPal=1;
+#ifndef NO_SYNC
+			else sync();
+#endif
 		}
 		else	ret = -1;
 		if (!ret)
diff --git a/platform/gp2x/emu.h b/platform/gp2x/emu.h
index f5dcee31..b487c51e 100644
--- a/platform/gp2x/emu.h
+++ b/platform/gp2x/emu.h
@@ -18,7 +18,7 @@ enum TPicoGameState {
 typedef struct {
 	char lastRomFile[512];
 	int EmuOpt;		// LSb->MSb: use_sram, show_fps, enable_sound, gzip_saves,
-					// squidgehack, save_cfg_on_exit, <unused>, 16_bit_mode
+					// squidgehack, no_save_cfg_on_exit, <unused>, 16_bit_mode
 					// craigix_ram, confirm_save, show_cd_leds
 					//
 	int PicoOpt;  // used for config saving only, see Pico.h
@@ -32,6 +32,7 @@ typedef struct {
 	int JoyBinds[4][32];
 	int PicoAutoRgnOrder;
 	int PicoCDBuffers;
+	int scaling; // 0=center, 1=hscale, 2=hvscale, 3=hsoftscale
 } currentConfig_t;
 
 extern char romFileName[];
@@ -52,4 +53,5 @@ int  emu_check_save_file(int slot);
 void emu_set_save_cbs(int gz);
 void emu_forced_frame(void);
 int  find_bios(int region, char **bios_file);
+void scaling_update(void);
 
diff --git a/platform/gp2x/gp2x.c b/platform/gp2x/gp2x.c
index 28c0a938..f138be63 100644
--- a/platform/gp2x/gp2x.c
+++ b/platform/gp2x/gp2x.c
@@ -52,19 +52,21 @@ void *gp2x_screen;
 #define FRAMEBUFF_ADDR2 (FRAMEBUFF_ADDR1+0x30000)
 #define FRAMEBUFF_ADDR3 (FRAMEBUFF_ADDR2+0x30000)
 
-static const int gp2x_screenaddrs[] = { FRAMEBUFF_ADDR0, FRAMEBUFF_ADDR1, FRAMEBUFF_ADDR2, FRAMEBUFF_ADDR3 };
+static const int gp2x_screenaddrs[4] = { FRAMEBUFF_ADDR0, FRAMEBUFF_ADDR1, FRAMEBUFF_ADDR2, FRAMEBUFF_ADDR3 };
+static int gp2x_screenaddrs_use[4];
 static unsigned short gp2x_screenaddr_old[4];
 
 
 /* video stuff */
 void gp2x_video_flip(void)
 {
-	unsigned short msw = (unsigned short)(gp2x_screenaddrs[screensel&3] >> 16);
+	unsigned short lsw = (unsigned short) gp2x_screenaddrs_use[screensel&3];
+	unsigned short msw = (unsigned short)(gp2x_screenaddrs_use[screensel&3] >> 16);
 
   	gp2x_memregs[0x2910>>1] = msw;
   	gp2x_memregs[0x2914>>1] = msw;
-	gp2x_memregs[0x290E>>1] = 0;
-  	gp2x_memregs[0x2912>>1] = 0;
+	gp2x_memregs[0x290E>>1] = lsw;
+  	gp2x_memregs[0x2912>>1] = lsw;
 
 	// jump to other buffer:
 	gp2x_screen = gp2x_screens[++screensel&3];
@@ -73,7 +75,7 @@ void gp2x_video_flip(void)
 /* doulblebuffered flip */
 void gp2x_video_flip2(void)
 {
-	unsigned short msw = (unsigned short)(gp2x_screenaddrs[screensel&1] >> 16);
+	unsigned short msw = (unsigned short)(gp2x_screenaddrs_use[screensel&1] >> 16);
 
   	gp2x_memregs[0x2910>>1] = msw;
   	gp2x_memregs[0x2914>>1] = msw;
@@ -113,10 +115,17 @@ void gp2x_video_setpalette(int *pal, int len)
 
 
 // TV Compatible function //
-void gp2x_video_RGB_setscaling(int W, int H)
+void gp2x_video_RGB_setscaling(int ln_offs, int W, int H)
 {
 	float escalaw, escalah;
 	int bpp = (gp2x_memregs[0x28DA>>1]>>9)&0x3;
+	unsigned short scalw;
+
+	// set offset
+	gp2x_screenaddrs_use[0] = gp2x_screenaddrs[0] + ln_offs * 320 * bpp;
+	gp2x_screenaddrs_use[1] = gp2x_screenaddrs[1] + ln_offs * 320 * bpp;
+	gp2x_screenaddrs_use[2] = gp2x_screenaddrs[2] + ln_offs * 320 * bpp;
+	gp2x_screenaddrs_use[3] = gp2x_screenaddrs[3] + ln_offs * 320 * bpp;
 
 	escalaw = 1024.0; // RGB Horiz LCD
 	escalah = 320.0; // RGB Vert LCD
@@ -131,7 +140,10 @@ void gp2x_video_RGB_setscaling(int W, int H)
 	}
 
 	// scale horizontal
-	gp2x_memregs[0x2906>>1]=(unsigned short)((float)escalaw *(W/320.0));
+	scalw = (unsigned short)((float)escalaw *(W/320.0));
+	/* if there is no horizontal scaling, vertical doesn't work. Here is a nasty wrokaround... */
+	if (H != 240 && W == 320) scalw--;
+	gp2x_memregs[0x2906>>1]=scalw;
 	// scale vertical
 	gp2x_memregl[0x2908>>2]=(unsigned long)((float)escalah *bpp *(H/240.0));
 }
@@ -304,6 +316,7 @@ void gp2x_init(void)
 	gp2x_screenaddr_old[2] = gp2x_memregs[0x2912>>1];
 	gp2x_screenaddr_old[3] = gp2x_memregs[0x2914>>1];
 
+	memcpy(gp2x_screenaddrs_use, gp2x_screenaddrs, sizeof(gp2x_screenaddrs));
 	gp2x_memset_all_buffers(0, 0, 320*240*2);
 
 	// snd
diff --git a/platform/gp2x/gp2x.h b/platform/gp2x/gp2x.h
index 9883b22a..349b8987 100644
--- a/platform/gp2x/gp2x.h
+++ b/platform/gp2x/gp2x.h
@@ -12,7 +12,7 @@ void gp2x_video_flip2(void);
 void gp2x_video_changemode(int bpp);
 void gp2x_video_changemode2(int bpp);
 void gp2x_video_setpalette(int *pal, int len);
-void gp2x_video_RGB_setscaling(int W, int H);
+void gp2x_video_RGB_setscaling(int ln_offs, int W, int H);
 void gp2x_video_wait_vsync(void);
 void gp2x_memcpy_buffers(int buffers, void *data, int offset, int len);
 void gp2x_memcpy_all_buffers(void *data, int offset, int len);
diff --git a/platform/gp2x/menu.c b/platform/gp2x/menu.c
index d29d6d08..f9c80654 100644
--- a/platform/gp2x/menu.c
+++ b/platform/gp2x/menu.c
@@ -286,7 +286,10 @@ static int scandir_cmp(const void *p1, const void *p2)
 	return alphasort(d1, d2);
 }
 
-static char *filter_exts[] = { ".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html", ".jpg", ".gpe" };
+static char *filter_exts[] = {
+	".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds",	"bcfg", ".txt", ".htm", "html",
+	".jpg", ".gpe", ".cue"
+};
 
 static int scandir_filter(const struct dirent *ent)
 {
@@ -864,16 +867,15 @@ static void draw_amenu_options(int menu_sel)
 	//memset(gp2x_screen, 0, 320*240);
 	gp2x_pd_clone_buffer2();
 
-	gp2x_text_out8(tl_x, y,       "Scale 32 column mode       %s", (currentConfig.PicoOpt&0x100)?"ON":"OFF"); // 0
-	gp2x_text_out8(tl_x, (y+=10), "Gamma correction           %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100); // 1
-	gp2x_text_out8(tl_x, (y+=10), "Emulate Z80                %s", (currentConfig.PicoOpt&0x004)?"ON":"OFF"); // 2
-	gp2x_text_out8(tl_x, (y+=10), "Emulate YM2612 (FM)        %s", (currentConfig.PicoOpt&0x001)?"ON":"OFF"); // 3
-	gp2x_text_out8(tl_x, (y+=10), "Emulate SN76496 (PSG)      %s", (currentConfig.PicoOpt&0x002)?"ON":"OFF"); // 4
-	gp2x_text_out8(tl_x, (y+=10), "gzip savestates            %s", (currentConfig.EmuOpt &0x008)?"ON":"OFF"); // 5
-	gp2x_text_out8(tl_x, (y+=10), "Don't save config on exit  %s", (currentConfig.EmuOpt &0x020)?"ON":"OFF"); // 6
+	gp2x_text_out8(tl_x, y,       "Gamma correction           %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100); // 0
+	gp2x_text_out8(tl_x, (y+=10), "Emulate Z80                %s", (currentConfig.PicoOpt&0x004)?"ON":"OFF"); // 1
+	gp2x_text_out8(tl_x, (y+=10), "Emulate YM2612 (FM)        %s", (currentConfig.PicoOpt&0x001)?"ON":"OFF"); // 2
+	gp2x_text_out8(tl_x, (y+=10), "Emulate SN76496 (PSG)      %s", (currentConfig.PicoOpt&0x002)?"ON":"OFF"); // 3
+	gp2x_text_out8(tl_x, (y+=10), "gzip savestates            %s", (currentConfig.EmuOpt &0x008)?"ON":"OFF"); // 4
+	gp2x_text_out8(tl_x, (y+=10), "Don't save config on exit  %s", (currentConfig.EmuOpt &0x020)?"ON":"OFF"); // 5
 	gp2x_text_out8(tl_x, (y+=10), "needs restart:");
-	gp2x_text_out8(tl_x, (y+=10), "craigix's RAM timings      %s", (currentConfig.EmuOpt &0x100)?"ON":"OFF"); // 8
-	gp2x_text_out8(tl_x, (y+=10), "squidgehack (now %s %s",   mms, (currentConfig.EmuOpt &0x010)?"ON":"OFF"); // 9
+	gp2x_text_out8(tl_x, (y+=10), "craigix's RAM timings      %s", (currentConfig.EmuOpt &0x100)?"ON":"OFF"); // 7
+	gp2x_text_out8(tl_x, (y+=10), "squidgehack (now %s %s",   mms, (currentConfig.EmuOpt &0x010)?"ON":"OFF"); // 8
 	gp2x_text_out8(tl_x, (y+=10), "Done");
 
 	// draw cursor
@@ -884,7 +886,7 @@ static void draw_amenu_options(int menu_sel)
 
 static void amenu_loop_options(void)
 {
-	int menu_sel = 0, menu_sel_max = 10;
+	int menu_sel = 0, menu_sel_max = 9;
 	unsigned long inp = 0;
 
 	for(;;)
@@ -895,21 +897,20 @@ static void amenu_loop_options(void)
 		if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
 		if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options
 			switch (menu_sel) {
-				case  0: currentConfig.PicoOpt^=0x100; break;
-				case  2: currentConfig.PicoOpt^=0x004; break;
-				case  3: currentConfig.PicoOpt^=0x001; break;
-				case  4: currentConfig.PicoOpt^=0x002; break;
-				case  5: currentConfig.EmuOpt ^=0x008; break;
-				case  6: currentConfig.EmuOpt ^=0x020; break;
-				case  8: currentConfig.EmuOpt ^=0x100; break;
-				case  9: currentConfig.EmuOpt ^=0x010; break;
-				case 10: return;
+				case  1: currentConfig.PicoOpt^=0x004; break;
+				case  2: currentConfig.PicoOpt^=0x001; break;
+				case  3: currentConfig.PicoOpt^=0x002; break;
+				case  4: currentConfig.EmuOpt ^=0x008; break;
+				case  5: currentConfig.EmuOpt ^=0x020; break;
+				case  7: currentConfig.EmuOpt ^=0x100; break;
+				case  8: currentConfig.EmuOpt ^=0x010; break;
+				case  9: return;
 			}
 		}
 		if(inp & (GP2X_X|GP2X_A)) return;
 		if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise
 			switch (menu_sel) {
-				case 1:
+				case 0:
 					while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {
 						currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;
 						if (currentConfig.gamma <   1) currentConfig.gamma =   1;
@@ -949,8 +950,8 @@ static const char *region_name(unsigned int code)
 
 static void draw_menu_options(int menu_sel)
 {
-	int tl_x = 25, tl_y = 40, y;
-	char monostereo[8], strframeskip[8], *strrend;
+	int tl_x = 25, tl_y = 32, y;
+	char monostereo[8], strframeskip[8], *strrend, *strscaling;
 
 	strcpy(monostereo, (currentConfig.PicoOpt&0x08)?"stereo":"mono");
 	if (currentConfig.Frameskip < 0)
@@ -963,27 +964,34 @@ static void draw_menu_options(int menu_sel)
 	} else {
 		strrend = " 8bit accurate";
 	}
+	switch (currentConfig.scaling) {
+		default: strscaling = "            OFF";   break;
+		case 1:  strscaling = "hw horizontal";     break;
+		case 2:  strscaling = "hw horiz. + vert."; break;
+		case 3:  strscaling = "sw horizontal";     break;
+	}
 
 	y = tl_y;
 	//memset(gp2x_screen, 0, 320*240);
 	gp2x_pd_clone_buffer2();
 
 	gp2x_text_out8(tl_x, y,       "Renderer:            %s", strrend); // 0
-	gp2x_text_out8(tl_x, (y+=10), "Accurate timing (slower)   %s", (currentConfig.PicoOpt&0x040)?"ON":"OFF"); // 1
-	gp2x_text_out8(tl_x, (y+=10), "Accurate sprites (slower)  %s", (currentConfig.PicoOpt&0x080)?"ON":"OFF"); // 2
-	gp2x_text_out8(tl_x, (y+=10), "Show FPS                   %s", (currentConfig.EmuOpt &0x002)?"ON":"OFF"); // 3
+	gp2x_text_out8(tl_x, (y+=10), "Scaling:       %s", strscaling);    // 1
+	gp2x_text_out8(tl_x, (y+=10), "Accurate timing (slower)   %s", (currentConfig.PicoOpt&0x040)?"ON":"OFF"); // 2
+	gp2x_text_out8(tl_x, (y+=10), "Accurate sprites (slower)  %s", (currentConfig.PicoOpt&0x080)?"ON":"OFF"); // 3
+	gp2x_text_out8(tl_x, (y+=10), "Show FPS                   %s", (currentConfig.EmuOpt &0x002)?"ON":"OFF"); // 4
 	gp2x_text_out8(tl_x, (y+=10), "Frameskip                  %s", strframeskip);
-	gp2x_text_out8(tl_x, (y+=10), "Enable sound               %s", (currentConfig.EmuOpt &0x004)?"ON":"OFF"); // 5
+	gp2x_text_out8(tl_x, (y+=10), "Enable sound               %s", (currentConfig.EmuOpt &0x004)?"ON":"OFF"); // 6
 	gp2x_text_out8(tl_x, (y+=10), "Sound Quality:     %5iHz %s",   currentConfig.PsndRate, monostereo);
-	gp2x_text_out8(tl_x, (y+=10), "Use ARM940 core for sound  %s", (currentConfig.PicoOpt&0x200)?"ON":"OFF"); // 7
-	gp2x_text_out8(tl_x, (y+=10), "6 button pad               %s", (currentConfig.PicoOpt&0x020)?"ON":"OFF"); // 8
+	gp2x_text_out8(tl_x, (y+=10), "Use ARM940 core for sound  %s", (currentConfig.PicoOpt&0x200)?"ON":"OFF"); // 8
+	gp2x_text_out8(tl_x, (y+=10), "6 button pad               %s", (currentConfig.PicoOpt&0x020)?"ON":"OFF"); // 9
 	gp2x_text_out8(tl_x, (y+=10), "Genesis Region:      %s",       region_name(currentConfig.PicoRegion));
-	gp2x_text_out8(tl_x, (y+=10), "Use SRAM/BRAM savestates   %s", (currentConfig.EmuOpt &0x001)?"ON":"OFF"); // 10
-	gp2x_text_out8(tl_x, (y+=10), "Confirm save overwrites    %s", (currentConfig.EmuOpt &0x200)?"ON":"OFF"); // 11
-	gp2x_text_out8(tl_x, (y+=10), "Save slot                  %i", state_slot); // 12
+	gp2x_text_out8(tl_x, (y+=10), "Use SRAM/BRAM savestates   %s", (currentConfig.EmuOpt &0x001)?"ON":"OFF"); // 11
+	gp2x_text_out8(tl_x, (y+=10), "Confirm save overwrites    %s", (currentConfig.EmuOpt &0x200)?"ON":"OFF"); // 12
+	gp2x_text_out8(tl_x, (y+=10), "Save slot                  %i", state_slot); // 13
 	gp2x_text_out8(tl_x, (y+=10), "GP2X CPU clocks            %iMhz", currentConfig.CPUclock);
 	gp2x_text_out8(tl_x, (y+=10), "[Sega/Mega CD options]");
-	gp2x_text_out8(tl_x, (y+=10), "[advanced options]");		// 15
+	gp2x_text_out8(tl_x, (y+=10), "[advanced options]");		// 16
 	gp2x_text_out8(tl_x, (y+=10), "Save cfg as default");
 	if (rom_data)
 		gp2x_text_out8(tl_x, (y+=10), "Save cfg for current game only");
@@ -1042,11 +1050,12 @@ static void menu_options_save(void)
 	} else {
 		actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;
 	}
+	scaling_update();
 }
 
 static int menu_loop_options(void)
 {
-	int menu_sel = 0, menu_sel_max = 16;
+	int menu_sel = 0, menu_sel_max = 17;
 	unsigned long inp = 0;
 
 	if (rom_data) menu_sel_max++;
@@ -1062,25 +1071,25 @@ static int menu_loop_options(void)
 		if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
 		if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options
 			switch (menu_sel) {
-				case  1: currentConfig.PicoOpt^=0x040; break;
-				case  2: currentConfig.PicoOpt^=0x080; break;
-				case  3: currentConfig.EmuOpt ^=0x002; break;
-				case  5: currentConfig.EmuOpt ^=0x004; break;
-				case  7: currentConfig.PicoOpt^=0x200; break;
-				case  8: currentConfig.PicoOpt^=0x020; break;
-				case 10: currentConfig.EmuOpt ^=0x001; break;
-				case 11: currentConfig.EmuOpt ^=0x200; break;
-				case 14: cd_menu_loop_options();
+				case  2: currentConfig.PicoOpt^=0x040; break;
+				case  3: currentConfig.PicoOpt^=0x080; break;
+				case  4: currentConfig.EmuOpt ^=0x002; break;
+				case  6: currentConfig.EmuOpt ^=0x004; break;
+				case  8: currentConfig.PicoOpt^=0x200; break;
+				case  9: currentConfig.PicoOpt^=0x020; break;
+				case 11: currentConfig.EmuOpt ^=0x001; break;
+				case 12: currentConfig.EmuOpt ^=0x200; break;
+				case 15: cd_menu_loop_options();
 					if (engineState == PGS_ReloadRom)
 						return 0; // test BIOS
 					break;
-				case 15: amenu_loop_options();    break;
-				case 16: // done (update and write)
+				case 16: amenu_loop_options();    break;
+				case 17: // done (update and write)
 					menu_options_save();
 					if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");
 					else strcpy(menuErrorMsg, "failed to write config");
 					return 1;
-				case 17: // done (update and write for current game)
+				case 18: // done (update and write for current game)
 					menu_options_save();
 					if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");
 					else strcpy(menuErrorMsg, "failed to write config");
@@ -1104,28 +1113,33 @@ static int menu_loop_options(void)
 						else if (  currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;
 					}
 					break;
-				case  4:
+				case  1:
+					currentConfig.scaling += (inp & GP2X_LEFT) ? -1 : 1;
+					if (currentConfig.scaling < 0) currentConfig.scaling = 0;
+					if (currentConfig.scaling > 3) currentConfig.scaling = 3;
+					break;
+				case  5:
 					currentConfig.Frameskip += (inp & GP2X_LEFT) ? -1 : 1;
 					if (currentConfig.Frameskip < 0)  currentConfig.Frameskip = -1;
 					if (currentConfig.Frameskip > 32) currentConfig.Frameskip = 32;
 					break;
-				case  6:
+				case  7:
 					if ((inp & GP2X_RIGHT) && currentConfig.PsndRate == 44100 && !(currentConfig.PicoOpt&0x08)) {
 						currentConfig.PsndRate = 8000;  currentConfig.PicoOpt|= 0x08;
 					} else if ((inp & GP2X_LEFT) && currentConfig.PsndRate == 8000 && (currentConfig.PicoOpt&0x08)) {
 						currentConfig.PsndRate = 44100; currentConfig.PicoOpt&=~0x08;
 					} else currentConfig.PsndRate = sndrate_prevnext(currentConfig.PsndRate, inp & GP2X_RIGHT);
 					break;
-				case  9:
+				case 10:
 					region_prevnext(inp & GP2X_RIGHT);
 					break;
-				case 12:
+				case 13:
 					if (inp & GP2X_RIGHT) {
 						state_slot++; if (state_slot > 9) state_slot = 0;
 					} else {state_slot--; if (state_slot < 0) state_slot = 9;
 					}
 					break;
-				case 13:
+				case 14:
 					while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {
 						currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;
 						if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;
@@ -1222,7 +1236,11 @@ static void menu_loop_root(void)
 	if (rom_data) menu_sel = menu_sel_min = 0;
 	if (PicoPatches) menu_sel_max = 9;
 
-	for(;;)
+	/* make sure action buttons are not pressed on entering menu */
+	draw_menu_root(menu_sel);
+	while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);
+
+	for (;;)
 	{
 		draw_menu_root(menu_sel);
 		inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT);
@@ -1324,7 +1342,7 @@ static void menu_gfx_prepare(void)
 
 	// switch to 8bpp
 	gp2x_video_changemode2(8);
-	gp2x_video_RGB_setscaling(320, 240);
+	gp2x_video_RGB_setscaling(0, 320, 240);
 	gp2x_video_flip2();
 }
 
diff --git a/platform/gp2x/version.h b/platform/gp2x/version.h
index 1725e8d1..cc93f892 100644
--- a/platform/gp2x/version.h
+++ b/platform/gp2x/version.h
@@ -1,2 +1,2 @@
-#define VERSION "1.31"
+#define VERSION "1.32"
 
diff --git a/platform/linux/Makefile b/platform/linux/Makefile
index 9a8f3df8..a01a529e 100644
--- a/platform/linux/Makefile
+++ b/platform/linux/Makefile
@@ -90,7 +90,7 @@ PicoDrive : $(OBJS) ../gp2x/helix/helix_mp3_x86.a
 
 ../../Pico/sound/ym2612.o : ../../Pico/sound/ym2612.c
 	@echo $@
-	@$(GCC) $(COPT_COMMON) $(DEFINC) -DEXTERNAL_YM2612 -c $< -o $@ # -mtune=arm940t
+	@$(GCC) $(COPT_COMMON) $(DEFINC) -c $< -o $@ # -mtune=arm940t -DEXTERNAL_YM2612
 
 # faked asm
 ../../Pico/Draw.o : ../../Pico/Draw.c
diff --git a/platform/linux/gp2x.c b/platform/linux/gp2x.c
index cc4db984..40cd7bc9 100644
--- a/platform/linux/gp2x.c
+++ b/platform/linux/gp2x.c
@@ -257,7 +257,7 @@ void gp2x_video_setpalette(int *pal, int len)
 	memcpy(current_pal, pal, len*4);
 }
 
-void gp2x_video_RGB_setscaling(int W, int H)
+void gp2x_video_RGB_setscaling(int v_offs, int W, int H)
 {
 }
 
diff --git a/platform/linux/port_config.h b/platform/linux/port_config.h
index 42be2649..765c0cd5 100644
--- a/platform/linux/port_config.h
+++ b/platform/linux/port_config.h
@@ -4,7 +4,7 @@
 #define PORT_CONFIG_H
 
 #define CPU_CALL
-#define NO_IONBF
+#define NO_SYNC
 
 // draw2.c
 #define START_ROW  0 // which row of tiles to start rendering at?
diff --git a/platform/readme.txt b/platform/readme.txt
index c658bddd..9436491c 100644
--- a/platform/readme.txt
+++ b/platform/readme.txt
@@ -214,6 +214,15 @@ Symbian:
 
 Changelog
 ---------
+1.32
+  + Added some new scaling options.
+  * Fixed DMA timing emulation (caused lock-ups for some genesis games).
+  * Idle loop detection was picking up wrong code and causing glitches, fixed.
+  * The ym2612 code on 940 now can handle multiple updates per frame
+    (fixes Thunger Force III "seiren" level drums for example).
+  * Memory handlers were ignoring some writes to PSG chip, fixed (missing sounds in
+    Popful Mail, Silpheed).
+
 1.31
   * Changed the way memory mode register is read (fixes Lunar 2, broken in 1.30).
   * Fixed TAS opcode on sub-68k side (fixes Batman games).
-- 
2.39.5