From b8a54c7a6f9086f2e98f79d1834a5130e414f931 Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 3 Apr 2025 21:08:07 +0200 Subject: [PATCH 01/39] core, fix interlace in arm assembly --- pico/draw_arm.S | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pico/draw_arm.S b/pico/draw_arm.S index d3f456ed..73ee07f7 100644 --- a/pico/draw_arm.S +++ b/pico/draw_arm.S @@ -836,7 +836,9 @@ DrawLayer: mov r0, sp mov r1, r9, lsr #29 - bl DrawStripInterlace @ struct TileStrip *ts, int plane_sh + mov r2, r9, lsr #8 + and r2, r2, #0xff + bl DrawStripInterlace @ struct TileStrip *ts, int plane_sh, int cellskip add sp, sp, #6*4 ldmfd sp!, {r4-r11,pc} From 634347ba6200023d3365793ce951aff482a36564 Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 3 Apr 2025 21:08:23 +0200 Subject: [PATCH 02/39] core, another layer A window rendering fix --- pico/draw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pico/draw.c b/pico/draw.c index 2d47998c..48cc8b8e 100644 --- a/pico/draw.c +++ b/pico/draw.c @@ -600,7 +600,7 @@ static void DrawWindow(int tstart, int tend, int prio, int sh, struct PicoVideo *pvid = &est->Pico->video; int tilex,ty,nametab,code,oldcode=-1,blank=-1; // The tile we know is blank int yshift,ymask; - u32 pack=0; + u32 pack=0, pal=0; u32 *hc=NULL, lflags=0; // referenced in DrawTile yshift = 4, ymask = 0x7; @@ -634,7 +634,7 @@ static void DrawWindow(int tstart, int tend, int prio, int sh, sh = (sh ? 0x80 : 0x00); // sh and low prio -> shadow for (; tilex < tend; tilex++) { - int dx, pal=0; + int dx; code = PicoMem.vram[nametab + tilex]; if ((code>>15) != prio) { @@ -652,7 +652,7 @@ static void DrawWindow(int tstart, int tend, int prio, int sh, sh = lflags; // sh and high prio -> no shadow (lflags to suppress warning) for (; tilex < tend; tilex++) { - int dx, pal=0; + int dx; code = PicoMem.vram[nametab + tilex]; if((code>>15) != prio) { From 26ecb2b6358fefba24e3d68b9eb2efba7f10d5ee Mon Sep 17 00:00:00 2001 From: notaz Date: Thu, 3 Apr 2025 02:33:00 +0300 Subject: [PATCH 03/39] pandora: fix readme and pxml version --- platform/pandora/Makefile | 1 + platform/pandora/make_pxml.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/pandora/Makefile b/platform/pandora/Makefile index fee9df86..8a534104 100644 --- a/platform/pandora/Makefile +++ b/platform/pandora/Makefile @@ -30,6 +30,7 @@ rel: ../../picodrive PicoDrive.sh picorestore \ rm -rf out mkdir out cp -r $^ out/ + mv out/README.md out/README $(PND_MAKE) -p PicoDrive_$(VER).pnd -d out -x out/PicoDrive.pxml -i out/PicoDrive.png -c FORCE: diff --git a/platform/pandora/make_pxml.sh b/platform/pandora/make_pxml.sh index 35d537a4..1b045459 100755 --- a/platform/pandora/make_pxml.sh +++ b/platform/pandora/make_pxml.sh @@ -7,7 +7,7 @@ test -f $verfile major=`head -n 1 $verfile | sed 's/[^0-9]*\([0-9]*\)\.\([0-9]*\).*/\1/g'` minor=`head -n 1 $verfile | sed 's/[^0-9]*\([0-9]*\)\.\([0-9]*\).*/\2/g'` # lame, I know.. -build=`git describe HEAD | grep -- - | sed -e 's/.*\-\(.*\)\-.*/\1/'` +build=`git describe --tags HEAD | grep -- - | sed -e 's/.*\-\(.*\)\-.*/\1/'` test -n "$build" && build_post="-$build" test -n "$build" || build=0 From 29b86990ee05dd926a82fa0cf566a2e7922b298b Mon Sep 17 00:00:00 2001 From: kub Date: Sat, 12 Apr 2025 21:27:09 +0200 Subject: [PATCH 04/39] core, minor port handling improvements --- pico/memory.c | 97 +++++++++++++++++++++++++++---------------------- pico/pico_cmn.c | 15 -------- pico/pico_int.h | 1 + 3 files changed, 55 insertions(+), 58 deletions(-) diff --git a/pico/memory.c b/pico/memory.c index e1a892ae..2f4ea4c8 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -27,9 +27,9 @@ static void xmap_set(uptr *map, int shift, u32 start_addr, u32 end_addr, const void *func_or_mh, int is_func) { #ifdef __clang__ - // workaround bug (segfault) in + // workaround bug (segfault) in // Apple LLVM version 4.2 (clang-425.0.27) (based on LLVM 3.2svn) - volatile + volatile #endif uptr addr = (uptr)func_or_mh; int mask = (1 << shift) - 1; @@ -236,9 +236,9 @@ static void m68k_unmapped_write16(u32 a, u32 d) void m68k_map_unmap(u32 start_addr, u32 end_addr) { #ifdef __clang__ - // workaround bug (segfault) in + // workaround bug (segfault) in // Apple LLVM version 4.2 (clang-425.0.27) (based on LLVM 3.2svn) - volatile + volatile #endif uptr addr; int shift = M68K_MEM_SHIFT; @@ -464,6 +464,12 @@ static u32 read_nothing(int i, u32 out_bits) typedef u32 (port_read_func)(int index, u32 out_bits); +int port_type[3] = { + PICO_INPUT_PAD_3BTN, + PICO_INPUT_PAD_3BTN, + PICO_INPUT_NOTHING +}; + static port_read_func *port_readers[3] = { read_pad_3btn, read_pad_3btn, @@ -472,23 +478,31 @@ static port_read_func *port_readers[3] = { static int padTHLatency[3]; static int padTLLatency[3]; +static int padTHTimeout[3]; static NOINLINE u32 port_read(int i) { u32 data_reg = PicoMem.ioports[i + 1]; u32 ctrl_reg = PicoMem.ioports[i + 4] | 0x80; + u32 cycles = SekCyclesDone(); u32 in, out; out = data_reg & ctrl_reg; + // controllers have a timeout on TH, they reset if not toggled for some time + if (CYCLES_GE(cycles, padTHTimeout[i])) { + padTHTimeout[i] = cycles; + Pico.m.padTHPhase[i] = 0; + } + // pull-ups: should be 0x7f, but Decap Attack has a bug where it temp. // disables output before doing TH-low read, so emulate RC filter for TH. // Decap Attack reportedly doesn't work on Nomad but works on most // other MD revisions (different pull-up strength?). u32 mask = 0x3f; - if (CYCLES_GE(SekCyclesDone(), padTHLatency[i])) { + if (CYCLES_GE(cycles, padTHLatency[i])) { mask |= 0x40; - padTHLatency[i] = SekCyclesDone(); + padTHLatency[i] = cycles; } out |= mask & ~ctrl_reg; @@ -496,8 +510,8 @@ static NOINLINE u32 port_read(int i) // Sega mouse uses the TL/TR lines for req/ack. For buggy drivers, make sure // there's some delay before ack is sent by taking over the new TL line level - if (CYCLES_GE(SekCyclesDone(), padTLLatency[i])) - padTLLatency[i] = SekCyclesDone(); + if (CYCLES_GE(cycles, padTLLatency[i])) + padTLLatency[i] = cycles; else in ^= 0x10; // TL @@ -517,35 +531,19 @@ void PicoSetInputDevice(int port, enum input_device device) if (port < 0 || port > 2) return; - if (port == 1 && port_readers[0] == read_pad_team) - func = read_nothing; - - else switch (device) { - case PICO_INPUT_PAD_3BTN: - func = read_pad_3btn; - break; - - case PICO_INPUT_PAD_6BTN: - func = read_pad_6btn; - break; - - case PICO_INPUT_PAD_TEAM: - func = read_pad_team; - break; - - case PICO_INPUT_PAD_4WAY: - func = read_pad_4way; - break; - - case PICO_INPUT_MOUSE: - func = read_pad_mouse; - break; - - default: - func = read_nothing; - break; + switch (device) { + case PICO_INPUT_PAD_3BTN: func = read_pad_3btn; break; + case PICO_INPUT_PAD_6BTN: func = read_pad_6btn; break; + case PICO_INPUT_PAD_TEAM: func = read_pad_team; break; + case PICO_INPUT_PAD_4WAY: func = read_pad_4way; break; + case PICO_INPUT_MOUSE: func = read_pad_mouse; break; + default: func = read_nothing; break; } + if (port == 1 && port_type[0] == PICO_INPUT_PAD_TEAM) + func = read_nothing; + + port_type[port] = device; port_readers[port] = func; } @@ -570,18 +568,20 @@ NOINLINE void io_ports_write(u32 a, u32 d) // for some controllers, changing TH/TR changes controller state if (1 <= a && a <= 2) { - Pico.m.padDelay[a - 1] = 0; - if (port_readers[a - 1] == read_pad_team) { + // there's a timeout for toggling the TH line + padTHTimeout[a - 1] = SekCyclesDone() + 25*488; + + if (port_type[a - 1] == PICO_INPUT_PAD_TEAM) { if (d & 0x40) Pico.m.padTHPhase[a - 1] = 0; else if ((d^PicoMem.ioports[a]) & 0x60) Pico.m.padTHPhase[a - 1]++; - } else if (port_readers[0] == read_pad_4way) { + } else if (port_type[0] == PICO_INPUT_PAD_4WAY) { if (a == 2 && ((PicoMem.ioports[a] ^ d) & 0x70)) Pico.m.padTHPhase[0] = 0; if (a == 1 && !(PicoMem.ioports[a] & 0x40) && (d & 0x40)) Pico.m.padTHPhase[0]++; - } else if (port_readers[a - 1] == read_pad_mouse) { + } else if (port_type[a - 1] == PICO_INPUT_MOUSE) { if ((d^PicoMem.ioports[a]) & 0x20) { if (Pico.m.padTHPhase[a - 1]) { // in readout? padTLLatency[a - 1] = SekCyclesDone() + 100; @@ -597,7 +597,7 @@ NOINLINE void io_ports_write(u32 a, u32 d) Pico.m.padTHPhase[a - 1]++; } - // after switching TH to input there's a latency before the pullup value is + // after switching TH to input there's a latency before the pullup value is // read back as input (see Decap Attack, not in Samurai Showdown, 32x WWF Raw) if (4 <= a && a <= 5) { if ((PicoMem.ioports[a] & 0x40) && !(d & 0x40) && !(PicoMem.ioports[a - 3] & 0x40)) @@ -625,6 +625,8 @@ int io_ports_pack(void *buf, size_t size) save_u16(buf, &b, PicoIn.padInt[i]); for (i = 0; i < 4; i++) save_s32(buf, &b, PicoIn.mouseInt[i]); + for (i = 0; i < ARRAY_SIZE(padTHTimeout); i++) + save_u32(buf, &b, padTHTimeout[i]); assert(b <= size); return b; } @@ -645,6 +647,15 @@ void io_ports_unpack(const void *buf, size_t size) PicoIn.padInt[i] = load_u16(buf, &b); for (i = 0; i < 4; i++) PicoIn.mouseInt[i] = load_s32(buf, &b); + + // convert pad delay from line count to samples + for (i = 0; i < ARRAY_SIZE(padTHTimeout); i++) { + if (b < size) + padTHTimeout[i] = load_u32(buf, &b); + else + padTHTimeout[i] = SekCyclesDone() - (25-Pico.m.padDelay[i]) * 488; + } + assert(b <= size); } @@ -675,7 +686,7 @@ void NOINLINE ctl_write_z80busreq(u32 d) pprof_start(m68k); PicoSyncZ80(SekCyclesDone()); pprof_end_sub(m68k); - granted = Pico.t.z80c_aim + 6; // M cycle is 3-6 cycles + granted = Pico.t.z80c_aim + 6; // M cycle is 3-6 cycles Pico.t.z80_busdelay += (Pico.t.z80c_cnt - granted) << 8; Pico.t.z80c_cnt = granted; } @@ -746,7 +757,7 @@ static u32 PicoRead8_sram(u32 a) // XXX: this is banking unfriendly if (a < Pico.romsize) return Pico.rom[MEM_BE2(a)]; - + return m68k_unmapped_read8(a); } @@ -835,7 +846,7 @@ static u32 PicoRead8_z80(u32 a) if ((a & 0x4000) == 0x0000) { d = PicoMem.zram[a & 0x1fff]; } else if ((a & 0x6000) == 0x4000) // 0x4000-0x5fff - d = ym2612_read_local_68k(); + d = ym2612_read_local_68k(); else { elprintf(EL_UIO|EL_ANOMALY, "68k bad read [%06x] @%06x", a, SekPc); d = (u8)PicoRead16_floating(a); diff --git a/pico/pico_cmn.c b/pico/pico_cmn.c index 00785223..d6083aa9 100644 --- a/pico/pico_cmn.c +++ b/pico/pico_cmn.c @@ -10,12 +10,6 @@ #define CYCLES_M68K_LINE 488 // suitable for both PAL/NTSC #define CYCLES_M68K_VINT_LAG 112 -// pad delay (for 6 button pads) -#define PAD_DELAY() { \ - if(Pico.m.padDelay[0]++ > 25) Pico.m.padTHPhase[0]=0; \ - if(Pico.m.padDelay[1]++ > 25) Pico.m.padTHPhase[1]=0; \ -} - // CPUS_RUN #ifndef CPUS_RUN #define CPUS_RUN(m68k_cycles) \ @@ -160,8 +154,6 @@ static int PicoFrameHints(void) Pico.m.scanline = y; pv->v_counter = PicoVideoGetV(y, 0); - PAD_DELAY(); - // H-Interrupts: if (--hint < 0) { @@ -217,8 +209,6 @@ static int PicoFrameHints(void) pv->v_counter = PicoVideoGetV(y, 0); memcpy(PicoIn.padInt, PicoIn.pad, sizeof(PicoIn.padInt)); - PAD_DELAY(); - // Last H-Int (normally): if (--hint < 0) { @@ -288,8 +278,6 @@ static int PicoFrameHints(void) Pico.m.scanline = y; pv->v_counter = PicoVideoGetV(y, 1); - PAD_DELAY(); - if (unlikely(pv->status & PVS_ACTIVE) && --hint < 0) { hint = pv->reg[10]; // Reload H-Int counter @@ -324,8 +312,6 @@ static int PicoFrameHints(void) Pico.m.scanline = y++; pv->v_counter = 0xff; - PAD_DELAY(); - if (unlikely(pv->status & PVS_ACTIVE)) { if (--hint < 0) { hint = pv->reg[10]; // Reload H-Int counter @@ -361,7 +347,6 @@ static int PicoFrameHints(void) return 0; } -#undef PAD_DELAY #undef CPUS_RUN // vim:shiftwidth=2:ts=2:expandtab diff --git a/pico/pico_int.h b/pico/pico_int.h index e2fd39ab..fc4792a4 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -760,6 +760,7 @@ void PicoWrite16_io(u32 a, u32 d); u32 PicoReadPad(int i, u32 mask); int io_ports_pack(void *buf, size_t size); void io_ports_unpack(const void *buf, size_t size); +extern int port_type[3]; // pico/memory.c PICO_INTERNAL void PicoMemSetupPico(void); From 30f199df947ea836a069bb460d8b5529bb190097 Mon Sep 17 00:00:00 2001 From: kub Date: Sat, 5 Apr 2025 09:10:20 +0200 Subject: [PATCH 05/39] core, add light gun support (light phaser, menacer, justifier) --- pico/memory.c | 64 +++++++++++++++++++++++++ pico/pico.c | 2 + pico/pico.h | 9 ++-- pico/pico_cmn.c | 9 ++++ pico/pico_int.h | 10 ++++ pico/sms.c | 22 +++++++++ pico/videoport.c | 65 +++++++++++++++++++++---- platform/common/config_file.c | 12 +++++ platform/common/emu.c | 75 +++++++++++++++++------------ platform/common/emu.h | 3 ++ platform/common/menu_pico.c | 90 +++++++++++++++++++++++++++++++---- platform/common/menu_pico.h | 6 +++ platform/linux/emu.c | 12 ++++- 13 files changed, 328 insertions(+), 51 deletions(-) diff --git a/pico/memory.c b/pico/memory.c index 2f4ea4c8..e9880c5b 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -457,6 +457,30 @@ static u32 read_pad_mouse(int i, u32 out_bits) return value; } +static u32 read_pad_menacer(int i, u32 out_bits) +{ + u32 pad = PicoIn.padInt[i]; // Get pad MXYZ SACB RLDU; NB not(!) inverted + u32 value; + + value = ((pad>>4) & 0x09) | ((pad>>3) & 0x4) | ((pad>>5) & 0x2); // .... SCAB + + value |= 0x40; // TH line is light sensor. Pretend there's always darkness... + return value; +} + +static u32 read_pad_justifier(int i, u32 out_bits) +{ + u32 pad = ~PicoIn.padInt[i]; // Get inverse of pad MXYZ SACB RLDU + u32 value = 0x30; // .111 0000, for ID + + // output data: .tgd ...., t = TH, g = gun select, d = disable + if (!(PicoMem.ioports[i+1] & out_bits & 0x70)) // blue gun, enabled + value |= ((pad>>6) & 0x03); // .011 ..SA + + value |= (out_bits & 0x40); + return value; +} + static u32 read_nothing(int i, u32 out_bits) { return 0xff; @@ -469,6 +493,7 @@ int port_type[3] = { PICO_INPUT_PAD_3BTN, PICO_INPUT_NOTHING }; +int port_lightgun; static port_read_func *port_readers[3] = { read_pad_3btn, @@ -518,6 +543,40 @@ static NOINLINE u32 port_read(int i) return (in & ~ctrl_reg) | (data_reg & ctrl_reg); } +// update ports +void PicoPortUpdate(void) +{ + if (port_lightgun || (Pico.m.hardware & PMS_HW_LG)) { + PicoIn.mouseInt[0] += PicoIn.mouse[0] - PicoIn.mouseInt[2]; + PicoIn.mouseInt[1] += PicoIn.mouse[1] - PicoIn.mouseInt[3]; + PicoIn.mouseInt[2] = PicoIn.mouse[0]; + PicoIn.mouseInt[3] = PicoIn.mouse[1]; + if (PicoIn.mouseInt[0] < 0) PicoIn.mouseInt[0] = 0; + if (PicoIn.mouseInt[0] > 320 ) PicoIn.mouseInt[0] = 320; + if (PicoIn.mouseInt[1] < 0) PicoIn.mouseInt[1] = 0; + if (PicoIn.mouseInt[1] > 240) PicoIn.mouseInt[1] = 240; + } +} + +void PicoPortTrigger(void) +{ + // TODO mx/my scaling for V28/V30, H32/H40, soft/hardscaling? + int mx = PicoIn.mouseInt[0] + PicoIn.gunx; + int my = PicoIn.mouseInt[1] + PicoIn.guny; + + if (unlikely(Pico.m.scanline == my) && port_lightgun && + ((PicoMem.ioports[4]|PicoMem.ioports[5]) & 0x80)) { + // there's a rather massive delay from VDP, TV, light sensor, infrared + // transmission, to the TH pin; it lasts well into the next line. + if (port_type[1] == PICO_INPUT_JUSTIFIER) { + // justifier should trigger only if gun enabled? + if (!(PicoMem.ioports[2] & 0x30)) + PicoVideoTriggerTH(mx + 66, my); + } else + PicoVideoTriggerTH(mx + 136, my); + } +} + // pad export for J-Cart u32 PicoReadPad(int i, u32 out_bits) { @@ -527,6 +586,7 @@ u32 PicoReadPad(int i, u32 out_bits) void PicoSetInputDevice(int port, enum input_device device) { port_read_func *func; + int is_lg = device == PICO_INPUT_LIGHT_GUN || device == PICO_INPUT_JUSTIFIER; if (port < 0 || port > 2) return; @@ -537,12 +597,16 @@ void PicoSetInputDevice(int port, enum input_device device) case PICO_INPUT_PAD_TEAM: func = read_pad_team; break; case PICO_INPUT_PAD_4WAY: func = read_pad_4way; break; case PICO_INPUT_MOUSE: func = read_pad_mouse; break; + case PICO_INPUT_LIGHT_GUN: func = read_pad_menacer; break; + case PICO_INPUT_JUSTIFIER: func = read_pad_justifier; break; default: func = read_nothing; break; } if (port == 1 && port_type[0] == PICO_INPUT_PAD_TEAM) func = read_nothing; + port_lightgun &= ~(1<hint_cnt; @@ -154,6 +155,8 @@ static int PicoFrameHints(void) Pico.m.scanline = y; pv->v_counter = PicoVideoGetV(y, 0); + PicoPortTick(); + // H-Interrupts: if (--hint < 0) { @@ -209,6 +212,8 @@ static int PicoFrameHints(void) pv->v_counter = PicoVideoGetV(y, 0); memcpy(PicoIn.padInt, PicoIn.pad, sizeof(PicoIn.padInt)); + PicoPortTick(); + // Last H-Int (normally): if (--hint < 0) { @@ -278,6 +283,8 @@ static int PicoFrameHints(void) Pico.m.scanline = y; pv->v_counter = PicoVideoGetV(y, 1); + PicoPortTick(); + if (unlikely(pv->status & PVS_ACTIVE) && --hint < 0) { hint = pv->reg[10]; // Reload H-Int counter @@ -312,6 +319,8 @@ static int PicoFrameHints(void) Pico.m.scanline = y++; pv->v_counter = 0xff; + PicoPortTick(); + if (unlikely(pv->status & PVS_ACTIVE)) { if (--hint < 0) { hint = pv->reg[10]; // Reload H-Int counter diff --git a/pico/pico_int.h b/pico/pico_int.h index fc4792a4..bda94faa 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -302,6 +302,7 @@ extern SH2 sh2s[2]; #define PVS_DMAFILL (1 << 20) // DMA fill is waiting for fill data #define PVS_DMABG (1 << 21) // background DMA operation is running #define PVS_FIFORUN (1 << 22) // FIFO is processing +#define PVS_HVLATCH (1 << 23) // next hv read is latched (for lightguns) struct PicoVideo { @@ -354,6 +355,7 @@ struct PicoMisc #define PMS_HW_FM 0x8 // FM sound #define PMS_HW_TMS 0x10 // assume TMS9918 #define PMS_HW_3D 0x20 // 3D glasses +#define PMS_HW_LG 0x40 // light phaser #define PMS_HW_FMUSED 0x80 // FM sound accessed #define PMS_MAP_AUTO 0 @@ -757,10 +759,16 @@ u32 PicoRead8_io(u32 a); u32 PicoRead16_io(u32 a); void PicoWrite8_io(u32 a, u32 d); void PicoWrite16_io(u32 a, u32 d); +void PicoPortUpdate(void); +void PicoPortTrigger(void); u32 PicoReadPad(int i, u32 mask); int io_ports_pack(void *buf, size_t size); void io_ports_unpack(const void *buf, size_t size); extern int port_type[3]; +extern int port_lightgun; + +#define PicoPortTick() \ + if (Pico.m.scanline == PicoIn.mouseInt[1] && port_lightgun) PicoPortTrigger() // pico/memory.c PICO_INTERNAL void PicoMemSetupPico(void); @@ -978,6 +986,7 @@ void PicoVideoFIFOMode(int active, int h40); int PicoVideoFIFOWrite(int count, int byte_p, unsigned sr_mask, unsigned sr_flags); void PicoVideoInit(void); void PicoVideoReset(void); +void PicoVideoTriggerTH(int x, int y); void PicoVideoSync(int skip); int PicoVideoSave(void *buf); void PicoVideoLoad(void *buf, int len); @@ -1024,6 +1033,7 @@ void PicoPowerMS(void); void PicoResetMS(void); void PicoMemSetupMS(void); void PicoStateLoadedMS(void); +void PicoPrepareMS(void); void PicoFrameMS(void); void PicoFrameDrawOnlyMS(void); int PicoPlayTape(const char *fname); diff --git a/pico/sms.c b/pico/sms.c index c46528de..7edd8add 100644 --- a/pico/sms.c +++ b/pico/sms.c @@ -503,6 +503,20 @@ static unsigned char z80_sms_in(unsigned short a) d = (d & ~0x08) | ((Pico.ms.io_ctl >> 3) & 0x08); if (Pico.ms.io_ctl & 0x08) d |= 0x80; // TH as input is unconnected if (Pico.ms.io_ctl & 0x02) d |= 0x40; + if (Pico.m.hardware & PMS_HW_LG) { // light phaser + // TODO mx/my scaling is wrong if V28/V30 mode is used? + int mx = (PicoIn.mouseInt[0]+PicoIn.gunx) * 256*((1LL<<32)/320) >> 32; + int my = (PicoIn.mouseInt[1]+PicoIn.guny) * 192*((1LL<<32)/224) >> 32; + int x = vdp_hcounter(z80_cyclesDone() - Pico.t.z80c_line_start); + int dx = 2*x - mx; + int dy = Pico.m.scanline - my; + int th = 0xc0; // TODO set bits according to port usage + d |= th; // TH input, high if no light detected + if (dy > -4 && dy < 4 && dx > -40 && dx < 40) { + d &= ~th; // TH falling -> save hcounter + Pico.ms.vdp_hlatch = (mx >> 1) + 24; + } + } } else { int i; // read kbd 4 bits kbd_update(); @@ -1223,6 +1237,13 @@ void PicoStateLoadedMS(void) memcpy(Pico.ms.carthw, carthw, 16); } +void PicoPrepareMS(void) +{ + Pico.m.hardware &= ~PMS_HW_LG; + if (port_type[0] == PICO_INPUT_LIGHT_GUN || port_type[1] == PICO_INPUT_LIGHT_GUN) + Pico.m.hardware |= PMS_HW_LG; +} + void PicoFrameMS(void) { struct PicoVideo *pv = &Pico.video; @@ -1236,6 +1257,7 @@ void PicoFrameMS(void) int y; PsndStartFrame(); + PicoPortUpdate(); // for SMS the pause button generates an NMI, for GG ths is not the case nmi = (PicoIn.pad[0] >> 7) & 1; diff --git a/pico/videoport.c b/pico/videoport.c index 583b2583..b0208744 100644 --- a/pico/videoport.c +++ b/pico/videoport.c @@ -405,7 +405,7 @@ int PicoVideoFIFOHint(void) // only need to refresh sprite position if we are synced if (Pico.est.DrawScanline == Pico.m.scanline && !(pv->status & SR_VB)) PicoDrawRefreshSprites(); - + // if CPU is waiting for the bus, advance CPU and FIFO until bus is free if (pv->status & PVS_CPUWR) burn = PicoVideoFIFODrain(4, lc, 0); @@ -1120,10 +1120,28 @@ PICO_INTERNAL_ASM u32 PicoVideoRead(u32 a) unsigned int c; u32 d; + // invalidate cached value if on a new scanline + if ((pv->status & PVS_HVLATCH) && pv->hv_latch >> 8 != Pico.m.scanline) + pv->status &= ~PVS_HVLATCH; + c = SekCyclesDone() - Pico.t.m68c_line_start; - if (pv->reg[0]&2) - d = pv->hv_latch; - else d = VdpFIFO.fifo_hcounts[c/clkdiv] | (pv->v_counter << 8); + if (pv->reg[0]&2) // latched value from TH transition + d = pv->hv_latch; + else if (pv->status & PVS_HVLATCH) { // cached value from light gun + c += 28; // estimate for 68k insn time + // return cached value, but add in irq latency; since the irq was fired + // at the start of the line, the latency in cycles already is in c. + d = pv->hv_latch & 0xff; + if (pv->reg[12]&1) { // H40/H32? + c = c * ((1LL*slots40<<32)/slcpu) >> 32; // slots since line start + if (d < gapstart40 && d+c >= gapstart40) d += gapend40-gapstart40; + } else { + c = c * ((1LL*slots32<<32)/slcpu) >> 32; + if (d < gapstart32 && d+c >= gapstart32) d += gapend32-gapstart32; + } + d = (pv->hv_latch & 0xff00) | ((d+c) & 0x00ff); + } else // freerunning + d = VdpFIFO.fifo_hcounts[c/clkdiv] | (pv->v_counter << 8); elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, pv->v_counter, SekCyclesDone(), SekPc); return d; @@ -1193,16 +1211,45 @@ unsigned char PicoVideoRead8HV_L(int is_from_z80) void PicoVideoReset(void) { - Pico.video.pending_ints=0; - Pico.video.reg[1] &= ~0x40; // TODO verify display disabled after reset - Pico.video.reg[10] = 0xff; // HINT is turned off after reset - Pico.video.status = 0x3428 | Pico.m.pal; // 'always set' bits | vblank | collision | pal + struct PicoVideo *pv = &Pico.video; + + pv->pending_ints=0; + pv->reg[1] &= ~0x40; // TODO verify display disabled after reset + pv->reg[10] = 0xff; // HINT is turned off after reset + pv->status = 0x3428 | Pico.m.pal; // 'always set' bits | vblank | collision | pal memset(&VdpFIFO, 0, sizeof(VdpFIFO)); Pico.m.dirtyPal = 1; PicoDrawBgcDMA(NULL, 0, 0, 0, 0); - PicoVideoFIFOMode(Pico.video.reg[1]&0x40, Pico.video.reg[12]&1); + PicoVideoFIFOMode(pv->reg[1]&0x40, pv->reg[12]&1); +} + +// Process triggering of the !HL pin at pixel x,y of screen +void PicoVideoTriggerTH(int x, int y) +{ + struct PicoVideo *pv = &Pico.video; + int slot; + + // TODO x/y scaling for V28/V30, H32/H40, soft/hardscaling? + if (!(pv->reg[12]&1)) { // H32, convert from 320 to 256 px per line + slot = (x * ((256LL<<32)/320) >> 32) / 2; + if (slot >= gapstart32) slot += gapend32-gapstart32; + } else { + slot = x / 2; + if (slot >= gapstart40) slot += gapend40-gapstart40; + } + + // This is only called at the start of a scanline, hence the irq isn't + // cycle correct. There are games reading hv unlatched in the irq handler, + // so latch mouse here, and fix the value up when the irq handler reads it. + pv->hv_latch = (slot & 0xff) | (y << 8); + pv->status |= PVS_HVLATCH; + + // irq here for speed reasons. It's early, but that shouldn't matter much. + // TODO is irq really lost if another irq is already pending? + if ((Pico.video.reg[11]&8) && SekIrqLevel < 2) + SekInterrupt(2); } void PicoVideoCacheSAT(int load) diff --git a/platform/common/config_file.c b/platform/common/config_file.c index 25286516..63a644ef 100644 --- a/platform/common/config_file.c +++ b/platform/common/config_file.c @@ -275,6 +275,7 @@ int config_readlrom(const char *fname) static int custom_read(menu_entry *me, const char *var, const char *val) { char *tmp; + int i; switch (me->id) { @@ -372,6 +373,17 @@ static int custom_read(menu_entry *me, const char *var, const char *val) currentConfig.keyboard = 1; return 1; + case MA_OPT_INPUT_DEV0: + for (i = 0; indev_names[i]; i++) + if (strcasecmp(val, indev_names[i]) == 0) + currentConfig.input_dev0 = i; + return 1; + case MA_OPT_INPUT_DEV1: + for (i = 0; indev_names[i]; i++) + if (strcasecmp(val, indev_names[i]) == 0) + currentConfig.input_dev1 = i; + return 1; + /* PSP */ case MA_OPT3_VSYNC: // XXX: use enum diff --git a/platform/common/emu.c b/platform/common/emu.c index 8e6ad540..ad286568 100644 --- a/platform/common/emu.c +++ b/platform/common/emu.c @@ -745,6 +745,8 @@ int emu_read_config(const char *rom_fname, int no_defaults) pemu_validate_config(); PicoIn.overclockM68k = currentConfig.overclock_68k; + PicoIn.gunx = currentConfig.gunx; + PicoIn.guny = currentConfig.guny; // some sanity checks if (currentConfig.volume < 0 || currentConfig.volume > 99) @@ -1202,7 +1204,7 @@ void run_events_pico(unsigned int events) return; /* handle other input modes using the pen */ - if (PicoIn.opt & POPT_EN_MOUSE) { + if (currentConfig.EmuOpt & EOPT_MOUSE) { pico_pen_x = PicoIn.mouse[0]; pico_pen_y = PicoIn.mouse[1]; } else { @@ -1324,7 +1326,7 @@ static void run_events_ui(unsigned int which) } if (which & PEV_GRAB_INPUT) { - if (PicoIn.opt & POPT_EN_MOUSE) { + if (currentConfig.EmuOpt & EOPT_MOUSE) { grab_mode = !grab_mode; in_update_analog(0, 2, &mouse_x); in_update_analog(0, 3, &mouse_y); @@ -1356,13 +1358,34 @@ static void run_events_ui(unsigned int which) engineState = PGS_Menu; } +static int map_pointer_buttons(int msbtns, int device) +{ + int buttons = 0; + + if ((PicoIn.AHW & PAHW_PICO) && device == PICO_INPUT_MOUSE) { + if (msbtns & 1) buttons |= 1< 4) pid = 1; player[strlen(player)-1] = pid + '0'; - e_menu_keyconfig[0].help = (pid >= 3 ? h_play34 : h_play12); + e_menu_keyconfig[x].help = (pid >= 3 ? h_play34 : h_play12); if (keys & PBTN_MOK) key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, pid-1); @@ -587,6 +588,7 @@ static const char *mgn_keyboard(int id, int *offs) static int key_config_keyboard(int id, int keys) { + int x = me_id2offset(e_menu_keyconfig, MA_CTRL_KEYBOARD); int kid = currentConfig.keyboard; #ifdef USE_SDL // TODO this info should come from platform! int k = 2; @@ -601,7 +603,7 @@ static int key_config_keyboard(int id, int keys) currentConfig.keyboard = kid; - e_menu_keyconfig[2].help = (currentConfig.keyboard == 2 ? h_kbd : NULL); + e_menu_keyconfig[x].help = (currentConfig.keyboard == 2 ? h_kbd : NULL); if (keys & PBTN_MOK) if (currentConfig.keyboard == 2) @@ -610,15 +612,79 @@ static int key_config_keyboard(int id, int keys) return 0; } +static const char *mgn_indev(int id, int *offs) +{ + *offs -= 4; + if (id == MA_OPT_INPUT_DEV0) + return indev_names[currentConfig.input_dev0]; + else return indev_names[currentConfig.input_dev1]; +} + +static int mh_indev(int id, int keys) +{ + int is0 = id == MA_OPT_INPUT_DEV0; + int *indev = is0 ? ¤tConfig.input_dev0 : ¤tConfig.input_dev1; + int x = me_id2offset(e_menu_keyconfig, MA_OPT_INPUT_DEV0); + + if (keys & PBTN_RIGHT) { + (*indev) ++; + // Justifier only in pad port 2 + if (is0 && *indev == PICO_INPUT_JUSTIFIER) + (*indev) ++; + // Team/4way only in pad port 1 + if (!is0 && *indev > PICO_INPUT_JUSTIFIER) + (*indev) --; + if (*indev >= PICO_INPUT_COUNT) + (*indev) --; + } + if (keys & PBTN_LEFT) { + (*indev) --; + if (is0 && *indev == PICO_INPUT_JUSTIFIER) + (*indev) --; + if (*indev < 0) + (*indev) ++; + } + if (*indev == PICO_INPUT_LIGHT_GUN) + e_menu_keyconfig[x + !is0].help = h_gun; + else e_menu_keyconfig[x + !is0].help = NULL; + + x = currentConfig.input_dev0 == PICO_INPUT_LIGHT_GUN || + currentConfig.input_dev1 == PICO_INPUT_LIGHT_GUN || + currentConfig.input_dev1 == PICO_INPUT_JUSTIFIER; + me_enable(e_menu_keyconfig, MA_CTRL_LIGHTGUN, x); + return 0; +} + +static menu_entry e_menu_gunconfig[] = +{ + mee_onoff ("Gun crosshair", MA_CTRL_GUN_CURSOR, currentConfig.EmuOpt, EOPT_GUN_CURSOR), + mee_range ("Gun x offset", MA_CTRL_GUN_XOFFS,currentConfig.gunx, -50, 50), + mee_range ("Gun y offset", MA_CTRL_GUN_YOFFS,currentConfig.guny, -50, 50), + mee_end, +}; + +static int gun_config_loop(int id, int keys) +{ + static int sel = 0; + + me_loop_d(e_menu_gunconfig, &sel, menu_draw_prep, NULL); + + PicoIn.gunx = currentConfig.gunx; + PicoIn.guny = currentConfig.guny; + + return 0; +} + static menu_entry e_menu_keyconfig[] = { mee_cust_nosave(player, MA_CTRL_PLAYER1, key_config_players, mgn_nothing), mee_handler_id("Emulator hotkeys", MA_CTRL_EMU, key_config_loop_wrap), mee_cust_h ("Keyboard", MA_CTRL_KEYBOARD, key_config_keyboard, mgn_keyboard, h_kbd), - mee_enum ("Input device 1", MA_OPT_INPUT_DEV0, currentConfig.input_dev0, indev0_names), - mee_enum ("Input device 2", MA_OPT_INPUT_DEV1, currentConfig.input_dev1, indev1_names), + mee_cust_h ("Input device 1", MA_OPT_INPUT_DEV0, mh_indev, mgn_indev, NULL), + mee_cust_h ("Input device 2", MA_OPT_INPUT_DEV1, mh_indev, mgn_indev, NULL), mee_range ("Turbo rate", MA_CTRL_TURBO_RATE, currentConfig.turbo_rate, 1, 30), mee_range ("Analog deadzone", MA_CTRL_DEADZONE, currentConfig.analog_deadzone, 1, 99), + mee_handler_id("Gun configuration", MA_CTRL_LIGHTGUN, gun_config_loop), mee_label (""), mee_label ("Input devices:"), mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name), @@ -648,8 +714,15 @@ static int menu_loop_keyconfig(int id, int keys) snprintf(h_kbd, sizeof(h_kbd), "%s - configure", in_get_key_name(-1, -PBTN_MOK)); player[strlen(player)-1] = '1'; - e_menu_keyconfig[0].help = h_play12; - e_menu_keyconfig[2].help = (currentConfig.keyboard == 2 ? h_kbd : NULL); + x = me_id2offset(e_menu_keyconfig, MA_CTRL_PLAYER1); + e_menu_keyconfig[x].help = h_play12; + x = me_id2offset(e_menu_keyconfig, MA_CTRL_KEYBOARD); + e_menu_keyconfig[x].help = (currentConfig.keyboard == 2 ? h_kbd : NULL); + + x = currentConfig.input_dev0 == PICO_INPUT_LIGHT_GUN || + currentConfig.input_dev1 == PICO_INPUT_LIGHT_GUN || + currentConfig.input_dev1 == PICO_INPUT_JUSTIFIER; + me_enable(e_menu_keyconfig, MA_CTRL_LIGHTGUN, x); me_loop_d(e_menu_keyconfig, &sel, menu_draw_prep, NULL); @@ -1756,6 +1829,7 @@ static menu_entry *e_menu_table[] = e_menu_sms_options, #endif e_menu_keyconfig, + e_menu_gunconfig, e_menu_hidden, }; diff --git a/platform/common/menu_pico.h b/platform/common/menu_pico.h index d5ed935b..d644918b 100644 --- a/platform/common/menu_pico.h +++ b/platform/common/menu_pico.h @@ -110,6 +110,10 @@ typedef enum MA_CTRL_PLAYER4, MA_CTRL_EMU, MA_CTRL_KEYBOARD, + MA_CTRL_LIGHTGUN, + MA_CTRL_GUN_CURSOR, + MA_CTRL_GUN_XOFFS, + MA_CTRL_GUN_YOFFS, MA_CTRL_TURBO_RATE, MA_CTRL_DEADZONE, MA_CTRL_DEV_FIRST, @@ -117,6 +121,8 @@ typedef enum MA_CTRL_DONE, } menu_id; +extern const char *indev_names[]; + void menu_init(void); void menu_loop(void); int menu_loop_tray(void); diff --git a/platform/linux/emu.c b/platform/linux/emu.c index faa577d3..22faae57 100644 --- a/platform/linux/emu.c +++ b/platform/linux/emu.c @@ -228,6 +228,16 @@ void pemu_finalize_frame(const char *fps, const char *notice) draw_pico_ptr(); } + // TODO correct ptr position for hard/soft/no scaling? +#define is_lightgun(d) (d == PICO_INPUT_LIGHT_GUN || d == PICO_INPUT_JUSTIFIER) + if ((currentConfig.EmuOpt & EOPT_GUN_CURSOR) && + (is_lightgun(currentConfig.input_dev0) || is_lightgun(currentConfig.input_dev1))) { + pico_inp_mode = 2; + pico_pen_x = PicoPicohw.pen_pos[0] = PicoIn.mouseInt[0]; + pico_pen_y = PicoPicohw.pen_pos[1] = PicoIn.mouseInt[1]; + draw_pico_ptr(); + } + // draw virtual keyboard on display if (kbd_mode && currentConfig.keyboard == 1 && vkbd) vkbd_draw(vkbd); @@ -486,7 +496,7 @@ void pemu_loop_prep(void) { apply_renderer(); plat_video_clear_buffers(); - plat_show_cursor(!(PicoIn.opt & POPT_EN_MOUSE)); + plat_show_cursor(!(currentConfig.EmuOpt & EOPT_MOUSE)); } void pemu_loop_end(void) From fabb02c350fffc40ea0d5477cccbede10220910f Mon Sep 17 00:00:00 2001 From: kub Date: Sun, 13 Apr 2025 09:29:53 +0200 Subject: [PATCH 06/39] ui, slightly change kbd config handling --- platform/common/menu_pico.c | 16 +++++----------- platform/common/menu_pico.h | 1 + 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c index 6d2bd237..dafe05a8 100644 --- a/platform/common/menu_pico.c +++ b/platform/common/menu_pico.c @@ -547,7 +547,6 @@ int key_config_kbd_loop(int id, int keys) const char *indev_names[] = { "none", "3 button pad", "6 button pad", "Mouse", "Light gun", "Justifier", "Team player", "4 way play", NULL }; static char h_play12[55]; -static char h_kbd[55]; static char h_play34[] = "Works only for Mega Drive/CD/32X games having\n" "support for Team player, 4 way play, or J-cart"; static char h_gun[] = "Light phaser for SMS, Menacer for Mega Drive"; @@ -588,7 +587,6 @@ static const char *mgn_keyboard(int id, int *offs) static int key_config_keyboard(int id, int keys) { - int x = me_id2offset(e_menu_keyconfig, MA_CTRL_KEYBOARD); int kid = currentConfig.keyboard; #ifdef USE_SDL // TODO this info should come from platform! int k = 2; @@ -603,11 +601,7 @@ static int key_config_keyboard(int id, int keys) currentConfig.keyboard = kid; - e_menu_keyconfig[x].help = (currentConfig.keyboard == 2 ? h_kbd : NULL); - - if (keys & PBTN_MOK) - if (currentConfig.keyboard == 2) - key_config_kbd_loop(MA_CTRL_KEYBOARD, 0); + me_enable(e_menu_keyconfig, MA_CTRL_KEYBOARD, currentConfig.keyboard == 2); return 0; } @@ -679,7 +673,8 @@ static menu_entry e_menu_keyconfig[] = { mee_cust_nosave(player, MA_CTRL_PLAYER1, key_config_players, mgn_nothing), mee_handler_id("Emulator hotkeys", MA_CTRL_EMU, key_config_loop_wrap), - mee_cust_h ("Keyboard", MA_CTRL_KEYBOARD, key_config_keyboard, mgn_keyboard, h_kbd), + mee_cust_h ("Keyboard", MA_OPT_KEYBOARD, key_config_keyboard, mgn_keyboard, NULL), + mee_handler_id("Keys configuration",MA_CTRL_KEYBOARD, key_config_kbd_loop), mee_cust_h ("Input device 1", MA_OPT_INPUT_DEV0, mh_indev, mgn_indev, NULL), mee_cust_h ("Input device 2", MA_OPT_INPUT_DEV1, mh_indev, mgn_indev, NULL), mee_range ("Turbo rate", MA_CTRL_TURBO_RATE, currentConfig.turbo_rate, 1, 30), @@ -711,13 +706,12 @@ static int menu_loop_keyconfig(int id, int keys) snprintf(l,sizeof(l),"%s", in_get_key_name(-1, -PBTN_LEFT)); snprintf(r,sizeof(r),"%s", in_get_key_name(-1, -PBTN_RIGHT)); snprintf(h_play12, sizeof(h_play12), "%s/%s - select, %s - configure", l, r, in_get_key_name(-1, -PBTN_MOK)); - snprintf(h_kbd, sizeof(h_kbd), "%s - configure", in_get_key_name(-1, -PBTN_MOK)); player[strlen(player)-1] = '1'; x = me_id2offset(e_menu_keyconfig, MA_CTRL_PLAYER1); e_menu_keyconfig[x].help = h_play12; - x = me_id2offset(e_menu_keyconfig, MA_CTRL_KEYBOARD); - e_menu_keyconfig[x].help = (currentConfig.keyboard == 2 ? h_kbd : NULL); + + me_enable(e_menu_keyconfig, MA_CTRL_KEYBOARD, currentConfig.keyboard == 2); x = currentConfig.input_dev0 == PICO_INPUT_LIGHT_GUN || currentConfig.input_dev1 == PICO_INPUT_LIGHT_GUN || diff --git a/platform/common/menu_pico.h b/platform/common/menu_pico.h index d644918b..cbb59fa5 100644 --- a/platform/common/menu_pico.h +++ b/platform/common/menu_pico.h @@ -34,6 +34,7 @@ typedef enum MA_OPT_ARM940_SOUND, MA_OPT_INPUT_DEV0, MA_OPT_INPUT_DEV1, + MA_OPT_KEYBOARD, MA_OPT_REGION, MA_OPT_CONFIRM_STATES, MA_OPT_SAVE_SLOT, From 6637acaefc2d72d608350b06fe915c88da16741e Mon Sep 17 00:00:00 2001 From: kub Date: Mon, 14 Apr 2025 20:54:05 +0200 Subject: [PATCH 07/39] core+ui, some small lightgun fixes --- pico/pico_int.h | 4 ++-- platform/common/menu_pico.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pico/pico_int.h b/pico/pico_int.h index bda94faa..2e1b1d05 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -767,8 +767,8 @@ void io_ports_unpack(const void *buf, size_t size); extern int port_type[3]; extern int port_lightgun; -#define PicoPortTick() \ - if (Pico.m.scanline == PicoIn.mouseInt[1] && port_lightgun) PicoPortTrigger() +#define PicoPortTick() if (port_lightgun && \ + Pico.m.scanline == PicoIn.mouseInt[1]+PicoIn.guny) PicoPortTrigger() // pico/memory.c PICO_INTERNAL void PicoMemSetupPico(void); diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c index dafe05a8..ffe56f1b 100644 --- a/platform/common/menu_pico.c +++ b/platform/common/menu_pico.c @@ -549,7 +549,8 @@ const char *indev_names[] = { "none", "3 button pad", "6 button pad", "Mouse", " static char h_play12[55]; static char h_play34[] = "Works only for Mega Drive/CD/32X games having\n" "support for Team player, 4 way play, or J-cart"; -static char h_gun[] = "Light phaser for SMS, Menacer for Mega Drive"; +static char h_gun[] = "Light Phaser for SMS, Menacer for Mega Drive\n" + "note: Justifier can only be connected to input 2"; static char player[] = "Player 1"; @@ -608,7 +609,7 @@ static int key_config_keyboard(int id, int keys) static const char *mgn_indev(int id, int *offs) { - *offs -= 4; + *offs = -4; if (id == MA_OPT_INPUT_DEV0) return indev_names[currentConfig.input_dev0]; else return indev_names[currentConfig.input_dev1]; From c0be6fbbc493994fbf51afce190a5890ab97fba4 Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 17 Apr 2025 23:16:21 +0200 Subject: [PATCH 08/39] ui, lightgun fixes --- platform/common/config_file.c | 2 +- platform/common/menu_pico.c | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/platform/common/config_file.c b/platform/common/config_file.c index 63a644ef..50623210 100644 --- a/platform/common/config_file.c +++ b/platform/common/config_file.c @@ -365,7 +365,7 @@ static int custom_read(menu_entry *me, const char *var, const char *val) currentConfig.max_skip = atoi(val); return 1; - case MA_CTRL_KEYBOARD: + case MA_OPT_KEYBOARD: currentConfig.keyboard = 0; if (strcasecmp(val, "physical") == 0) currentConfig.keyboard = 2; diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c index ffe56f1b..b75d6d25 100644 --- a/platform/common/menu_pico.c +++ b/platform/common/menu_pico.c @@ -550,7 +550,7 @@ static char h_play12[55]; static char h_play34[] = "Works only for Mega Drive/CD/32X games having\n" "support for Team player, 4 way play, or J-cart"; static char h_gun[] = "Light Phaser for SMS, Menacer for Mega Drive\n" - "note: Justifier can only be connected to input 2"; + "note: Justifier can only be selected on input 2"; static char player[] = "Player 1"; @@ -580,10 +580,12 @@ static int key_config_players(int id, int keys) static const char *mgn_keyboard(int id, int *offs) { - static char *kbds[] = { "OFF", "virtual", "physical" }; + static const char *kbds[] = { "OFF", "virtual", "physical" }; + const char *kbd = kbds[currentConfig.keyboard]; if (currentConfig.keyboard < 0 || currentConfig.keyboard > 2) - return kbds[0]; - return kbds[currentConfig.keyboard]; + kbd = kbds[0]; + *offs = 3-strlen(kbd); + return kbd; } static int key_config_keyboard(int id, int keys) @@ -609,10 +611,12 @@ static int key_config_keyboard(int id, int keys) static const char *mgn_indev(int id, int *offs) { - *offs = -4; + const char *dev; if (id == MA_OPT_INPUT_DEV0) - return indev_names[currentConfig.input_dev0]; - else return indev_names[currentConfig.input_dev1]; + dev = indev_names[currentConfig.input_dev0]; + else dev = indev_names[currentConfig.input_dev1]; + *offs = 3-strlen(dev); + return dev; } static int mh_indev(int id, int keys) From 605616dbb5cfd54434e99b5736188e86ba8aaeb0 Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 17 Apr 2025 23:20:00 +0200 Subject: [PATCH 09/39] 32x, improve vint irq handling --- pico/32x/32x.c | 23 ++++++++++++++++++++--- pico/32x/draw.c | 1 - pico/pico_int.h | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pico/32x/32x.c b/pico/32x/32x.c index f7d58be5..fb2f8b92 100644 --- a/pico/32x/32x.c +++ b/pico/32x/32x.c @@ -324,7 +324,7 @@ static void p32x_start_blank(void) Pico32xSwapDRAM(Pico32x.pending_fb ^ P32XV_FS); } - p32x_trigger_irq(NULL, Pico.t.m68c_aim, P32XI_VINT); + p32x_schedule_vint(NULL, Pico.t.m68c_aim); p32x_sh2_poll_event(msh2.poll_addr, &msh2, SH2_STATE_VPOLL, Pico.t.m68c_aim); p32x_sh2_poll_event(ssh2.poll_addr, &ssh2, SH2_STATE_VPOLL, Pico.t.m68c_aim); } @@ -336,7 +336,7 @@ static void p32x_end_blank(void) if ((Pico32x.vdp_regs[0] & P32XV_Mx) != 0) // no forced blanking Pico32x.vdp_regs[0x0a/2] &= ~P32XV_PEN; // no palette access if (!(Pico32x.sh2_regs[0] & 0x80)) { - // NB must precede VInt per hw manual, min 4 SH-2 cycles to pass Mars Check + // TODO must be early by min 4 SH-2 cycles to pass Mars Check? Pico32x.hint_counter = (int)(-1.5*0x10); p32x_schedule_hint(NULL, Pico.t.m68c_aim); } @@ -363,6 +363,17 @@ void p32x_schedule_hint(SH2 *sh2, unsigned int m68k_cycles) p32x_event_schedule(m68k_cycles, P32X_EVENT_HINT, after); } +void p32x_schedule_vint(SH2 *sh2, unsigned int m68k_cycles) +{ + // according to 32X hw manual, HInt and VInt are tied to HBLANK and VBLANK. + // Moreover, VBLANK is 27 px (224 mclk, 32 68k cycles) after HBLANK. + // TODO check if that matches HBLANK/VBLANK on the cartridge slot? + if (sh2 != NULL) + p32x_event_schedule_sh2(sh2, P32X_EVENT_VINT, 32); + else + p32x_event_schedule(m68k_cycles, P32X_EVENT_VINT, 32); +} + /* events */ static void fillend_event(unsigned int now) { @@ -377,6 +388,11 @@ static void hint_event(unsigned int now) p32x_schedule_hint(NULL, now); } +static void vint_event(unsigned int now) +{ + p32x_trigger_irq(NULL, now, P32XI_VINT); +} + typedef void (event_cb)(unsigned int now); /* times are in m68k (7.6MHz) cycles */ @@ -386,6 +402,7 @@ static event_cb *p32x_event_cbs[P32X_EVENT_COUNT] = { p32x_pwm_irq_event, // P32X_EVENT_PWM fillend_event, // P32X_EVENT_FILLEND hint_event, // P32X_EVENT_HINT + vint_event, // P32X_EVENT_VINT }; // schedule event at some time 'after', in m68k clocks @@ -512,7 +529,7 @@ void p32x_sync_other_sh2(SH2 *sh2, unsigned int m68k_target) } #define STEP_LS 24 -#define STEP_N 528 // at least one line (488) +#define STEP_N 192 // NFL; TODO at least a scanline (489) for good performance? #define sync_sh2s_normal p32x_sync_sh2s //#define sync_sh2s_lockstep p32x_sync_sh2s diff --git a/pico/32x/draw.c b/pico/32x/draw.c index 4426f3a5..00e5ddf2 100644 --- a/pico/32x/draw.c +++ b/pico/32x/draw.c @@ -129,7 +129,6 @@ void FinalizeLine32xRGB555(int sh, int line, struct PicoEState *est) unsigned short *dst = est->DrawLineDest; unsigned short *pal = Pico32xMem->pal_native; unsigned char *pmd = est->HighCol + 8; - unsigned short *palmd = est->HighPal; unsigned short *dram, *p32x; unsigned char mdbg; int h32 = !(Pico.video.reg[12] & 0x1); diff --git a/pico/pico_int.h b/pico/pico_int.h index 2e1b1d05..da5f6d69 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -1058,6 +1058,7 @@ enum p32x_event { P32X_EVENT_PWM, P32X_EVENT_FILLEND, P32X_EVENT_HINT, + P32X_EVENT_VINT, P32X_EVENT_COUNT, }; extern unsigned int p32x_event_times[P32X_EVENT_COUNT]; @@ -1081,6 +1082,7 @@ void p32x_reset_sh2s(void); void p32x_event_schedule(unsigned int now, enum p32x_event event, int after); void p32x_event_schedule_sh2(SH2 *sh2, enum p32x_event event, int after); void p32x_schedule_hint(SH2 *sh2, unsigned int m68k_cycles); +void p32x_schedule_vint(SH2 *sh2, unsigned int m68k_cycles); #define p32x_sh2_ready(sh2, cycles) \ (CYCLES_GT(cycles,sh2->m68krcycles_done) && \ From 67a026a4e0ef0ec059dbede16bbde30e16f23a79 Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 17 Apr 2025 23:22:36 +0200 Subject: [PATCH 10/39] core rendering, fix for sprite parsing if masked --- pico/draw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pico/draw.c b/pico/draw.c index 48cc8b8e..526f18e4 100644 --- a/pico/draw.c +++ b/pico/draw.c @@ -1245,7 +1245,6 @@ static NOINLINE void ParseSprites(int max_lines, int limit) { unsigned char *p = &HighLnSpr[y][0]; int cnt = p[0] & 0x7f; - if (p[1] & SPRL_MASKED) continue; // masked? if (p[3] >= max_line_sprites) continue; // sprite limit? p[3] ++; @@ -1258,6 +1257,8 @@ static NOINLINE void ParseSprites(int max_lines, int limit) } p[2] += w; + if (p[1] & SPRL_MASKED) continue; // masked? + if (sx == -0x78) { if (p[1] & (SPRL_HAVE_X|SPRL_TILE_OVFL)) p[1] |= SPRL_MASKED; // masked, no more sprites for this line From 93679c117fa5691464e26523fd9b8230b5d475c6 Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 17 Apr 2025 23:23:59 +0200 Subject: [PATCH 11/39] core, fix a crash in vdp dma --- pico/videoport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pico/videoport.c b/pico/videoport.c index b0208744..ff32c104 100644 --- a/pico/videoport.c +++ b/pico/videoport.c @@ -603,7 +603,7 @@ static void DmaSlow(int len, u32 source) !((source ^ (source + len-1)) & ~mask)) { // most used DMA mode - memcpy((char *)r + a, base + (source & mask), len * 2); + memcpy((char *)r + (u16)a, base + (source & mask), len * 2); a += len * 2; break; } From d39573e4cc23460198bdc36476302d8ad6c94ba6 Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 18 Apr 2025 12:58:18 +0200 Subject: [PATCH 12/39] platform, mouse and lightgun only for sdl --- platform/common/menu_pico.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c index b75d6d25..5b7f2584 100644 --- a/platform/common/menu_pico.c +++ b/platform/common/menu_pico.c @@ -627,19 +627,32 @@ static int mh_indev(int id, int keys) if (keys & PBTN_RIGHT) { (*indev) ++; +#ifdef USE_SDL // TODO this info should come from platform! // Justifier only in pad port 2 if (is0 && *indev == PICO_INPUT_JUSTIFIER) (*indev) ++; // Team/4way only in pad port 1 if (!is0 && *indev > PICO_INPUT_JUSTIFIER) (*indev) --; +#else + while (*indev >= PICO_INPUT_MOUSE && *indev <= PICO_INPUT_JUSTIFIER) + (*indev) ++; + // Team/4way only in pad port 1 + while (!is0 && *indev >= PICO_INPUT_MOUSE) + (*indev) --; +#endif if (*indev >= PICO_INPUT_COUNT) (*indev) --; } if (keys & PBTN_LEFT) { (*indev) --; +#ifdef USE_SDL // TODO this info should come from platform! if (is0 && *indev == PICO_INPUT_JUSTIFIER) (*indev) --; +#else + while (*indev >= PICO_INPUT_MOUSE && *indev <= PICO_INPUT_JUSTIFIER) + (*indev) --; +#endif if (*indev < 0) (*indev) ++; } From d2037daec05476ff825bf2ee8fd18a9b73ef8264 Mon Sep 17 00:00:00 2001 From: kub Date: Sun, 20 Apr 2025 17:31:54 +0200 Subject: [PATCH 13/39] core, fix input port handling regression --- pico/memory.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pico/memory.c b/pico/memory.c index e9880c5b..a406e1e5 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -516,7 +516,7 @@ static NOINLINE u32 port_read(int i) // controllers have a timeout on TH, they reset if not toggled for some time if (CYCLES_GE(cycles, padTHTimeout[i])) { - padTHTimeout[i] = cycles; + padTHTimeout[i] = cycles + 0x70000000; Pico.m.padTHPhase[i] = 0; } @@ -609,6 +609,8 @@ void PicoSetInputDevice(int port, enum input_device device) port_lightgun |= is_lg< Date: Fri, 25 Apr 2025 22:27:52 +0200 Subject: [PATCH 14/39] 32x, improve cpu synchronisation --- cpu/sh2/compiler.c | 1 - pico/32x/32x.c | 3 +++ pico/32x/memory.c | 35 ++++++++++++++++++++--------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cpu/sh2/compiler.c b/cpu/sh2/compiler.c index e925638f..14cc3d51 100644 --- a/cpu/sh2/compiler.c +++ b/cpu/sh2/compiler.c @@ -4651,7 +4651,6 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id) FLUSH_CYCLES(sr); rcache_get_reg_arg(0, GET_Rn(), NULL); tmp = emit_memhandler_read(0); - sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL); emith_clr_t_cond(sr); emith_cmp_r_imm(tmp, 0); emith_set_t_cond(sr, DCOND_EQ); diff --git a/pico/32x/32x.c b/pico/32x/32x.c index fb2f8b92..2956724b 100644 --- a/pico/32x/32x.c +++ b/pico/32x/32x.c @@ -166,6 +166,7 @@ void p32x_reset_sh2s(void) sh2_reset(&ssh2); sh2_peripheral_reset(&msh2); sh2_peripheral_reset(&ssh2); + msh2.state = ssh2.state = 0; // if we don't have BIOS set, perform it's work here. // MSH2 @@ -241,6 +242,7 @@ void PicoReset32x(void) { if (PicoIn.AHW & PAHW_32X) { p32x_trigger_irq(NULL, SekCyclesDone(), P32XI_VRES); + p32x_m68k_poll_event(0, -1); p32x_sh2_poll_event(msh2.poll_addr, &msh2, SH2_IDLE_STATES, SekCyclesDone()); p32x_sh2_poll_event(ssh2.poll_addr, &ssh2, SH2_IDLE_STATES, SekCyclesDone()); p32x_pwm_ctl_changed(); @@ -349,6 +351,7 @@ void p32x_schedule_hint(SH2 *sh2, unsigned int m68k_cycles) { // rather rough, 32x hint is useless in practice int after; + if (!((Pico32x.sh2irq_mask[0] | Pico32x.sh2irq_mask[1]) & 4)) return; // nobody cares if (!(Pico32x.sh2_regs[0] & 0x80) && (Pico.video.status & PVS_VB2)) diff --git a/pico/32x/memory.c b/pico/32x/memory.c index 12c3f737..f85b115f 100644 --- a/pico/32x/memory.c +++ b/pico/32x/memory.c @@ -184,7 +184,7 @@ static NOINLINE void sh2s_sync_on_read(SH2 *sh2, unsigned cycles) // This is used to correctly deliver syncronisation data to the 3 cpus. The // fifo stores 16 bit values, 8/32 bit accesses must be adapted accordingly. #define PFIFO_SZ 4 -#define PFIFO_CNT 8 +#define PFIFO_CNT 16 struct sh2_poll_fifo { u32 cycles; u32 a; @@ -198,7 +198,7 @@ static NOINLINE u32 sh2_poll_read(u32 a, u32 d, unsigned int cycles, SH2* sh2) int hix = (a >> 1) % PFIFO_CNT; struct sh2_poll_fifo *fifo = sh2_poll_fifo[hix]; struct sh2_poll_fifo *p; - int cpu = sh2 ? sh2->is_slave : -1; + int cpu = 1 << (sh2 ? sh2->is_slave : 2), age = sh2 ? 60 : 360; unsigned idx; a &= ~0x20000000; // ignore writethrough bit @@ -208,14 +208,14 @@ static NOINLINE u32 sh2_poll_read(u32 a, u32 d, unsigned int cycles, SH2* sh2) p = &fifo[idx]; idx = (idx+1) % PFIFO_SZ; - if (cpu != p->cpu) { - if (CYCLES_GT(cycles, p->cycles+60)) { // ~180 sh2 cycles, Spiderman + if (p->cpu & cpu) { + if (CYCLES_GT(cycles, p->cycles+age)) { // ~180 sh2 cycles, Spiderman // drop older fifo stores that may cause synchronisation problems. - p->a = -1; + p->cpu &= ~cpu; } else if (p->a == a) { // replace current data with fifo value and discard fifo entry d = p->d; - p->a = -1; + p->cpu &= ~cpu; break; } } @@ -228,7 +228,7 @@ static NOINLINE void sh2_poll_write(u32 a, u32 d, unsigned int cycles, SH2 *sh2) int hix = (a >> 1) % PFIFO_CNT; struct sh2_poll_fifo *fifo = sh2_poll_fifo[hix]; struct sh2_poll_fifo *q; - int cpu = sh2 ? sh2->is_slave : -1; + int cpu = 1 << (sh2 ? sh2->is_slave : 2); unsigned rd = sh2_poll_rd[hix], wr = sh2_poll_wr[hix]; unsigned idx, nrd; @@ -238,8 +238,8 @@ static NOINLINE void sh2_poll_write(u32 a, u32 d, unsigned int cycles, SH2 *sh2) for (idx = nrd = wr; idx != rd; ) { idx = (idx-1) % PFIFO_SZ; q = &fifo[idx]; - if (q->a == a && q->cpu != cpu) { q->a = -1; } - if (q->a != -1) { nrd = idx; } + if (q->a == a && (q->cpu & cpu)) { q->cpu = 0; } + if (q->cpu) { nrd = idx; } } rd = nrd; @@ -248,12 +248,12 @@ static NOINLINE void sh2_poll_write(u32 a, u32 d, unsigned int cycles, SH2 *sh2) // NB this can take an eternity on m68k: mov.b , needs // 28 m68k-cycles (~80 sh2-cycles) to complete (observed in Metal Head) q = &fifo[(sh2_poll_wr[hix]-1) % PFIFO_SZ]; - if (rd != wr && q->a == a && !CYCLES_GT(cycles,q->cycles + (cpu<0 ? 30:4))) { + if (rd != wr && q->a == a && !CYCLES_GT(cycles,q->cycles + (!sh2 ? 30:4))) { q->d = d; } else { // store write to poll address in fifo - fifo[wr] = - (struct sh2_poll_fifo){ .cycles = cycles, .a = a, .d = d, .cpu = cpu }; + fifo[wr] = (struct sh2_poll_fifo) + { .cycles = cycles, .a = a, .d = d, .cpu = ~cpu & 7 }; wr = (wr+1) % PFIFO_SZ; if (wr == rd) // fifo overflow, discard oldest value @@ -419,8 +419,10 @@ static void p32x_reg_write8(u32 a, u32 d) if ((d ^ r[0]) & ~d & P32XS_ADEN) { d |= P32XS_nRES; Pico32xShutdown(); - } else if ((d ^ r[0]) & d & P32XS_nRES) + } else if ((d ^ r[0]) & d & P32XS_nRES) { + p32x_sync_sh2s(SekCyclesDone()); p32x_reset_sh2s(); + } REG8IN16(r, 0x01) &= ~(P32XS_nRES|P32XS_ADEN); REG8IN16(r, 0x01) |= d & (P32XS_nRES|P32XS_ADEN); return; @@ -587,8 +589,10 @@ static void p32x_reg_write16(u32 a, u32 d) if ((d ^ r[0]) & ~d & P32XS_ADEN) { d |= P32XS_nRES; Pico32xShutdown(); - } else if ((d ^ r[0]) & d & P32XS_nRES) + } else if ((d ^ r[0]) & d & P32XS_nRES) { + p32x_sync_sh2s(SekCyclesDone()); p32x_reset_sh2s(); + } r[0] &= ~(P32XS_FM|P32XS_nRES|P32XS_ADEN); r[0] |= d & (P32XS_FM|P32XS_nRES|P32XS_ADEN); return; @@ -758,7 +762,7 @@ static u32 p32x_sh2reg_read16(u32 a, SH2 *sh2) cycles = sh2_cycles_done_m68k(sh2); sh2s_sync_on_read(sh2, cycles); return sh2_poll_read(a, Pico32x.sh2_regs[4 / 2], cycles, sh2); - case 0x06/2: + case 0x06/2: // DREQ ctrl return (r[a / 2] & ~P32XS_FULL) | 0x4000; case 0x08/2: // DREQ src case 0x0a/2: @@ -944,6 +948,7 @@ static void p32x_sh2reg_write16(u32 a, u32 d, SH2 *sh2) goto irls; case 0x1a/2: Pico32x.regs[2 / 2] &= ~(1 << sh2->is_slave); + p32x_sync_other_sh2(sh2, sh2_cycles_done_m68k(sh2)); p32x_update_cmd_irq(sh2, 0); return; case 0x1c/2: From 5ad7996e63d2710409ec4681ac4b766e29a67903 Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 25 Apr 2025 22:33:50 +0200 Subject: [PATCH 15/39] core, fix input port handling (ASAN report, port timeout) --- pico/memory.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pico/memory.c b/pico/memory.c index a406e1e5..98b5157c 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -515,7 +515,7 @@ static NOINLINE u32 port_read(int i) out = data_reg & ctrl_reg; // controllers have a timeout on TH, they reset if not toggled for some time - if (CYCLES_GE(cycles, padTHTimeout[i])) { + if (CYCLES_GE(cycles, padTHTimeout[i]) && i < 2) { padTHTimeout[i] = cycles + 0x70000000; Pico.m.padTHPhase[i] = 0; } @@ -635,6 +635,8 @@ NOINLINE void io_ports_write(u32 a, u32 d) if (1 <= a && a <= 2) { // there's a timeout for toggling the TH line + if (CYCLES_GE(SekCyclesDone(), padTHTimeout[a - 1])) + Pico.m.padTHPhase[a - 1] = 0; padTHTimeout[a - 1] = SekCyclesDone() + 25*488; if (port_type[a - 1] == PICO_INPUT_PAD_TEAM) { From 7102a71ba632915563529d504469094792f777a8 Mon Sep 17 00:00:00 2001 From: kub Date: Mon, 5 May 2025 22:49:07 +0200 Subject: [PATCH 16/39] platform sdl, handle resize events outside of libpicofe update libpicofe, enables 2x video overlay mode in menu --- platform/common/plat_sdl.c | 20 +++++++++++++++----- platform/libpicofe | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c index 4f8249f6..8fe111de 100644 --- a/platform/common/plat_sdl.c +++ b/platform/common/plat_sdl.c @@ -474,15 +474,14 @@ static void plat_sdl_resize(int w, int h) { // take over new settings #if defined(__OPENDINGUX__) - if (currentConfig.vscaling != EOPT_SCALE_HW && - plat_sdl_screen->w == 320 && plat_sdl_screen->h == 480) { + if (currentConfig.vscaling != EOPT_SCALE_HW && w == 320 && h == 480) { g_menuscreen_h = 240; g_menuscreen_w = 320; } else #endif { - g_menuscreen_h = plat_sdl_screen->h; - g_menuscreen_w = plat_sdl_screen->w; + g_menuscreen_h = h & ~3; + g_menuscreen_w = w & ~3; #if 0 // auto resizing may be nice, but creates problems on some SDL platforms if (!plat_sdl_overlay && !plat_sdl_gl_active && plat_sdl_is_windowed() && !plat_sdl_is_fullscreen()) { @@ -510,6 +509,17 @@ static void plat_sdl_quit(void) exit(1); } +static void plat_sdl_handler(void *event_) +{ + SDL_Event *event = event_; + + if (event->type == SDL_VIDEORESIZE) + // handle resize here to avoid the internal plat_sdl_change_video_mode + plat_sdl_resize(event->resize.w, event->resize.h); + else + plat_sdl_event_handler(event_); +} + void plat_init(void) { int ret; @@ -561,7 +571,7 @@ void plat_init(void) in_sdl_platform_data.joy_map = in_sdl_joy_map, in_sdl_platform_data.key_names = in_sdl_key_names, in_sdl_platform_data.kbd_map = in_sdl_kbd_map, - in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler); + in_sdl_init(&in_sdl_platform_data, plat_sdl_handler); in_probe(); // create an artificial resize event to initialize mouse scaling diff --git a/platform/libpicofe b/platform/libpicofe index c3031dbf..80612f5f 160000 --- a/platform/libpicofe +++ b/platform/libpicofe @@ -1 +1 @@ -Subproject commit c3031dbfda557f7058756583b329b59ea92a72dc +Subproject commit 80612f5f31a3201e6c7f710109a9c8b48f6bb757 From 18f4c0fb822c2df22fe024ffc7c29bccff4ac656 Mon Sep 17 00:00:00 2001 From: kub Date: Tue, 6 May 2025 22:02:29 +0200 Subject: [PATCH 17/39] platform sdl, fix regression --- platform/common/plat_sdl.c | 47 +++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c index 8fe111de..1cea2445 100644 --- a/platform/common/plat_sdl.c +++ b/platform/common/plat_sdl.c @@ -26,6 +26,8 @@ static void *shadow_fb; static int shadow_size; static struct area { int w, h; } area; +static int resized; + static struct in_pdata in_sdl_platform_data; static int hide_cursor; @@ -205,8 +207,8 @@ static int clear_buf_cnt, clear_stat_cnt; static void resize_buffers(void) { // make sure the shadow buffers are big enough in case of resize - if (shadow_size < g_menuscreen_w * g_menuscreen_h * 2) { - shadow_size = g_menuscreen_w * g_menuscreen_h * 2; + if (shadow_size < g_menuscreen_pp * g_menuscreen_h * 2) { + shadow_size = g_menuscreen_pp * g_menuscreen_h * 2; shadow_fb = realloc(shadow_fb, shadow_size); g_menubg_ptr = realloc(g_menubg_ptr, shadow_size); } @@ -268,8 +270,6 @@ void plat_video_set_shadow(int w, int h) void plat_video_flip(void) { - resize_buffers(); - if (plat_sdl_overlay != NULL) { SDL_Rect dstrect = { 0, 0, plat_sdl_screen->w, plat_sdl_screen->h }; @@ -296,19 +296,7 @@ void plat_video_flip(void) SDL_UnlockSurface(plat_sdl_screen); SDL_Flip(plat_sdl_screen); - // take over resized settings for the physical SDL surface - if ((plat_sdl_screen->w != g_menuscreen_w || - plat_sdl_screen->h != g_menuscreen_h) && plat_sdl_is_windowed() && - SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) { - plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, -1); - SDL_WM_GrabInput(SDL_GRAB_OFF); - g_menuscreen_pp = plat_sdl_screen->pitch/2; - - // force upper layer to use new dimensions - plat_video_set_shadow(g_screen_width, g_screen_height); - plat_video_set_buffer(g_screen_ptr); - rendstatus_old = -1; - } else if (!copy) { + if (!copy) { g_screen_ppitch = plat_sdl_screen->pitch/2; g_screen_ptr = plat_sdl_screen->pixels; plat_video_set_buffer(g_screen_ptr); @@ -323,13 +311,21 @@ void plat_video_flip(void) } } - // for overlay/gl modes buffer ptr may change on resize - if ((plat_sdl_overlay || plat_sdl_gl_active) && - (g_screen_ptr != shadow_fb || g_screen_ppitch != g_screen_width)) { - g_screen_ppitch = g_screen_width; - g_screen_ptr = shadow_fb; + // take over resized settings for the physical SDL surface + if (resized && plat_sdl_is_windowed() && + SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) { + plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, -1); + SDL_WM_GrabInput(SDL_GRAB_OFF); + g_menuscreen_pp = plat_sdl_screen->pitch/2; + resize_buffers(); + + // force upper layer to use new dimensions + plat_video_set_shadow(g_screen_width, g_screen_height); plat_video_set_buffer(g_screen_ptr); + rendstatus_old = -1; + resized = 0; } + if (clear_stat_cnt) { unsigned short *d = (unsigned short *)g_screen_ptr + g_screen_ppitch * g_screen_height; int l = g_screen_ppitch * 8; @@ -438,7 +434,6 @@ void plat_video_loop_prepare(void) // take over any new vout settings area = (struct area) { 0, 0 }; plat_sdl_change_video_mode(0, 0, 0); - resize_buffers(); // switch over to scaled output if available, but keep the aspect ratio if (plat_sdl_overlay || plat_sdl_gl_active) @@ -477,9 +472,10 @@ static void plat_sdl_resize(int w, int h) if (currentConfig.vscaling != EOPT_SCALE_HW && w == 320 && h == 480) { g_menuscreen_h = 240; g_menuscreen_w = 320; + resized = 1; } else #endif - { + if (g_menuscreen_w != (w & ~3) || g_menuscreen_h != (h & ~3)){ g_menuscreen_h = h & ~3; g_menuscreen_w = w & ~3; #if 0 // auto resizing may be nice, but creates problems on some SDL platforms @@ -498,9 +494,8 @@ static void plat_sdl_resize(int w, int h) } } #endif + resized = 1; } - - rendstatus_old = -1; } static void plat_sdl_quit(void) From 7b6b78fd1d4847696e2255b7b9e0a07845df70ef Mon Sep 17 00:00:00 2001 From: kub Date: Wed, 7 May 2025 19:39:29 +0200 Subject: [PATCH 18/39] core, fix sound disable option --- pico/memory.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pico/memory.c b/pico/memory.c index 98b5157c..e1379ce7 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -942,8 +942,7 @@ static void PicoWrite8_z80(u32 a, u32 d) return; } if ((a & 0x6000) == 0x4000) { // FM Sound - if (PicoIn.opt & POPT_EN_FM) - ym2612_write_local(a & 3, d & 0xff, 0); + ym2612_write_local(a & 3, d & 0xff, 0); return; } // TODO: probably other VDP access too? Maybe more mirrors? @@ -1688,8 +1687,7 @@ static unsigned char z80_md_bank_read(unsigned short a) static void z80_md_ym2612_write(unsigned int a, unsigned char data) { - if (PicoIn.opt & POPT_EN_FM) - ym2612_write_local(a, data, 1); + ym2612_write_local(a, data, 1); } static void z80_md_vdp_br_write(unsigned int a, unsigned char data) From 2f95b8124534657b491a14d1dc83490d82908fcf Mon Sep 17 00:00:00 2001 From: kub Date: Wed, 7 May 2025 19:50:58 +0200 Subject: [PATCH 19/39] 32x, improve cpu synchronisation --- pico/32x/32x.c | 13 +++++++------ pico/32x/memory.c | 7 ++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pico/32x/32x.c b/pico/32x/32x.c index 2956724b..068b1866 100644 --- a/pico/32x/32x.c +++ b/pico/32x/32x.c @@ -219,6 +219,7 @@ void Pico32xInit(void) void PicoPower32x(void) { memset(&Pico32x, 0, sizeof(Pico32x)); + memset(p32x_event_times, 0, sizeof(p32x_event_times)); Pico32x.regs[0] = P32XS_REN|P32XS_nRES; // verified Pico32x.regs[0x10/2] = 0xffff; @@ -241,10 +242,10 @@ void PicoUnload32x(void) void PicoReset32x(void) { if (PicoIn.AHW & PAHW_32X) { - p32x_trigger_irq(NULL, SekCyclesDone(), P32XI_VRES); + p32x_trigger_irq(NULL, Pico.t.m68c_aim, P32XI_VRES); p32x_m68k_poll_event(0, -1); - p32x_sh2_poll_event(msh2.poll_addr, &msh2, SH2_IDLE_STATES, SekCyclesDone()); - p32x_sh2_poll_event(ssh2.poll_addr, &ssh2, SH2_IDLE_STATES, SekCyclesDone()); + p32x_sh2_poll_event(msh2.poll_addr, &msh2, SH2_IDLE_STATES, Pico.t.m68c_aim); + p32x_sh2_poll_event(ssh2.poll_addr, &ssh2, SH2_IDLE_STATES, Pico.t.m68c_aim); p32x_pwm_ctl_changed(); p32x_timers_recalc(); } @@ -712,11 +713,11 @@ void Pico32xStateLoaded(int is_early) if (CYCLES_GE(sh2s[0].m68krcycles_done - Pico.t.m68c_aim, 500) || CYCLES_GE(sh2s[1].m68krcycles_done - Pico.t.m68c_aim, 500)) - sh2s[0].m68krcycles_done = sh2s[1].m68krcycles_done = SekCyclesDone(); - p32x_update_irls(NULL, SekCyclesDone()); + sh2s[0].m68krcycles_done = sh2s[1].m68krcycles_done = Pico.t.m68c_aim; + p32x_update_irls(NULL, Pico.t.m68c_aim); p32x_timers_recalc(); p32x_pwm_state_loaded(); - p32x_run_events(SekCyclesDone()); + p32x_run_events(Pico.t.m68c_aim); // TODO wakeup CPUs for now. poll detection stuff must go to the save state! p32x_m68k_poll_event(0, -1); diff --git a/pico/32x/memory.c b/pico/32x/memory.c index f85b115f..eac7be0a 100644 --- a/pico/32x/memory.c +++ b/pico/32x/memory.c @@ -184,7 +184,7 @@ static NOINLINE void sh2s_sync_on_read(SH2 *sh2, unsigned cycles) // This is used to correctly deliver syncronisation data to the 3 cpus. The // fifo stores 16 bit values, 8/32 bit accesses must be adapted accordingly. #define PFIFO_SZ 4 -#define PFIFO_CNT 16 +#define PFIFO_CNT 8 struct sh2_poll_fifo { u32 cycles; u32 a; @@ -947,8 +947,8 @@ static void p32x_sh2reg_write16(u32 a, u32 d, SH2 *sh2) Pico32x.sh2irqi[sh2->is_slave] &= ~P32XI_HINT; goto irls; case 0x1a/2: - Pico32x.regs[2 / 2] &= ~(1 << sh2->is_slave); p32x_sync_other_sh2(sh2, sh2_cycles_done_m68k(sh2)); + Pico32x.regs[2 / 2] &= ~(1 << sh2->is_slave); p32x_update_cmd_irq(sh2, 0); return; case 0x1c/2: @@ -2539,7 +2539,8 @@ void Pico32xMemStateLoaded(void) ssh2.poll_addr = ssh2.poll_cycles = ssh2.poll_cnt = 0; memset(sh2_poll_fifo, 0, sizeof(sh2_poll_fifo)); - sh2_drc_flush_all(); + if (Pico32x.emu_flags & P32XF_DRC_ROM_C) + sh2_drc_flush_all(); } // vim:shiftwidth=2:ts=2:expandtab From f36fd9857c620ce03a6a681e0b5e35e72e1a5a74 Mon Sep 17 00:00:00 2001 From: kub Date: Wed, 7 May 2025 23:39:01 +0200 Subject: [PATCH 20/39] core, streamline divide by multiplying with inverse --- pico/32x/32x.c | 2 +- pico/cd/memory.c | 2 +- pico/cd/pcm.c | 2 +- pico/memory.c | 8 ++++---- pico/pico_port.h | 8 ++++++++ pico/sms.c | 8 ++++---- pico/videoport.c | 2 +- 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/pico/32x/32x.c b/pico/32x/32x.c index 068b1866..2b054c5c 100644 --- a/pico/32x/32x.c +++ b/pico/32x/32x.c @@ -282,7 +282,7 @@ void Pico32xDrawSync(SH2 *sh2) // the fast renderer isn't operating on a line-by-line base if (sh2 && !(PicoIn.opt & POPT_ALT_RENDERER)) { unsigned int cycle = (sh2 ? sh2_cycles_done_m68k(sh2) : SekCyclesDone()); - int line = ((cycle - Pico.t.m68c_frame_start) * (long long)((1LL<<32)/488.5)) >> 32; + int line = DIVQ32(cycle - Pico.t.m68c_frame_start, 488.5); if (Pico32x.sync_line < line && line < (Pico.video.reg[1] & 8 ? 240 : 224)) { // make sure the MD image is also sync'ed to this line for merging diff --git a/pico/cd/memory.c b/pico/cd/memory.c index b3838905..5c783df0 100644 --- a/pico/cd/memory.c +++ b/pico/cd/memory.c @@ -101,7 +101,7 @@ u32 pcd_stopwatch_read(int sub) { // ugh... stopwatch runs 384 cycles per step, divide by mult with inverse u32 d = sub ? SekCyclesDoneS68k() : pcd_cycles_m68k_to_s68k(SekCyclesDone()); - d = ((d - Pico_mcd->m.stopwatch_base_c) * ((1LL << 32) / 384)) >> 32; + d = DIVQ32(d - Pico_mcd->m.stopwatch_base_c, 384); return d & 0x0fff; } diff --git a/pico/cd/pcm.c b/pico/cd/pcm.c index abb65f6c..8a1cbd46 100644 --- a/pico/cd/pcm.c +++ b/pico/cd/pcm.c @@ -62,7 +62,7 @@ void pcd_pcm_sync(unsigned int to) if ((int)(to - cycles) < 384) return; - steps = (to - cycles) * ((1LL << 32) / 384) >> 32; + steps = DIVQ32(to - cycles, 384); if (Pico_mcd->pcm_mixpos + steps > PCM_MIXBUF_LEN) // shouldn't happen, but occasionally does steps = PCM_MIXBUF_LEN - Pico_mcd->pcm_mixpos; diff --git a/pico/memory.c b/pico/memory.c index e1379ce7..9e523cf3 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -1328,7 +1328,7 @@ void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new) * Starting a timer takes place at the next tick, so xcycles needs to be * rounded up to that: t = next tick# = (xcycles / TICK_ZCYCLES) + 1 */ - unsigned t = ((xcycles * (((1LL<<32)/TIMER_A_TICK_ZCYCLES)+1))>>32) + 1; + unsigned t = DIVQ32(xcycles, TIMER_A_TICK_ZCYCLES) + 1; Pico.t.timer_a_next_oflow = t*TIMER_A_TICK_ZCYCLES + Pico.t.timer_a_step; } } @@ -1351,7 +1351,7 @@ void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new) * reset by loading timer b. The first run of timer b after loading is * therefore shorter by up to 15 ticks. */ - unsigned t = ((xcycles * (((1LL<<32)/TIMER_A_TICK_ZCYCLES)+1))>>32) + 1; + unsigned t = DIVQ32(xcycles, TIMER_A_TICK_ZCYCLES) + 1; int step = Pico.t.timer_b_step - TIMER_A_TICK_ZCYCLES*(t&15); Pico.t.timer_b_next_oflow = t*TIMER_A_TICK_ZCYCLES + step; } @@ -1526,9 +1526,9 @@ int ym2612_pack_timers(void *buf, size_t size) if (Pico.t.ym2612_busy > 0) busy = cycles_z80_to_68k(Pico.t.ym2612_busy); if (Pico.t.timer_a_next_oflow != TIMER_NO_OFLOW) - tat = ((Pico.t.timer_a_step - Pico.t.timer_a_next_oflow) * ((1LL<<32)/TIMER_A_TICK_ZCYCLES+1))>>16; + tat = MULQ((Pico.t.timer_a_step - Pico.t.timer_a_next_oflow), INVQ32(TIMER_A_TICK_ZCYCLES), 16); if (Pico.t.timer_b_next_oflow != TIMER_NO_OFLOW) - tbt = ((Pico.t.timer_b_step - Pico.t.timer_b_next_oflow) * ((1LL<<32)/TIMER_B_TICK_ZCYCLES+1))>>16; + tbt = MULQ((Pico.t.timer_b_step - Pico.t.timer_b_next_oflow), INVQ32(TIMER_B_TICK_ZCYCLES), 16); elprintf(EL_YMTIMER, "save: timer a %i/%i", tat >> 16, tac); elprintf(EL_YMTIMER, "save: timer b %i/%i", tbt >> 16, tbc); diff --git a/pico/pico_port.h b/pico/pico_port.h index 41a4ce2f..82c17bd1 100644 --- a/pico/pico_port.h +++ b/pico/pico_port.h @@ -80,4 +80,12 @@ (((v)<<8)&0xff0000)|(u32)((v)<<24)) #endif +#define INVQ(n,m) (long long)((1LL<<(m))/(n)+1) // to replace div by mult with inverse +#define MULQ(n,d,m) ((n)*(d) >> (m)) +#define DIVQ(n,d,m) ((n)*INVQ32(d) >> (m)) + +#define INVQ32(n) INVQ(n,32) +#define MULQ32(n,d) MULQ(n,d,32) +#define DIVQ32(n,d) DIVQ(n,d,32) + #endif // PICO_PORT_INCLUDED diff --git a/pico/sms.c b/pico/sms.c index 7edd8add..c04e5ada 100644 --- a/pico/sms.c +++ b/pico/sms.c @@ -505,8 +505,8 @@ static unsigned char z80_sms_in(unsigned short a) if (Pico.ms.io_ctl & 0x02) d |= 0x40; if (Pico.m.hardware & PMS_HW_LG) { // light phaser // TODO mx/my scaling is wrong if V28/V30 mode is used? - int mx = (PicoIn.mouseInt[0]+PicoIn.gunx) * 256*((1LL<<32)/320) >> 32; - int my = (PicoIn.mouseInt[1]+PicoIn.guny) * 192*((1LL<<32)/224) >> 32; + int mx = DIVQ32((PicoIn.mouseInt[0]+PicoIn.gunx) * 256, 320); + int my = DIVQ32((PicoIn.mouseInt[1]+PicoIn.guny) * 192, 224); int x = vdp_hcounter(z80_cyclesDone() - Pico.t.z80c_line_start); int dx = 2*x - mx; int dy = Pico.m.scanline - my; @@ -1389,7 +1389,7 @@ int PicoPlayTape(const char *fname) } pt->cycles_sample = (Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15) / rate; - pt->cycles_mult = (1LL<<32) / pt->cycles_sample; + pt->cycles_mult = INVQ32(pt->cycles_sample); pt->cycle = Pico.t.z80c_aim; pt->phase = Pico.t.z80c_aim; pt->pause = 0; @@ -1432,7 +1432,7 @@ int PicoRecordTape(const char *fname) } pt->cycles_sample = (Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15) / rate; - pt->cycles_mult = (1LL<<32) / pt->cycles_sample; + pt->cycles_mult = INVQ32(pt->cycles_sample); pt->cycle = Pico.t.z80c_aim; pt->phase = Pico.t.z80c_aim; pt->pause = 0; diff --git a/pico/videoport.c b/pico/videoport.c index ff32c104..7a126d5a 100644 --- a/pico/videoport.c +++ b/pico/videoport.c @@ -1233,7 +1233,7 @@ void PicoVideoTriggerTH(int x, int y) // TODO x/y scaling for V28/V30, H32/H40, soft/hardscaling? if (!(pv->reg[12]&1)) { // H32, convert from 320 to 256 px per line - slot = (x * ((256LL<<32)/320) >> 32) / 2; + slot = DIVQ32(x * 256, 320) / 2; if (slot >= gapstart32) slot += gapend32-gapstart32; } else { slot = x / 2; From 484095e30996b1378a2dbad9a996d298ed5b7698 Mon Sep 17 00:00:00 2001 From: kub Date: Wed, 7 May 2025 23:59:21 +0200 Subject: [PATCH 21/39] 32x, revisit wd timer --- cpu/sh2/sh2.h | 1 - pico/32x/32x.c | 39 ++++++++---------- pico/32x/sh2soc.c | 101 +++++++++++++++++++++++++++++++--------------- pico/pico_int.h | 12 +++--- 4 files changed, 93 insertions(+), 60 deletions(-) diff --git a/cpu/sh2/sh2.h b/cpu/sh2/sh2.h index 95c5658f..64c9fc59 100644 --- a/cpu/sh2/sh2.h +++ b/cpu/sh2/sh2.h @@ -50,7 +50,6 @@ typedef struct SH2_ #define SH2_STATE_CPOLL (1 << 2) // polling comm regs #define SH2_STATE_VPOLL (1 << 3) // polling VDP #define SH2_STATE_RPOLL (1 << 4) // polling address in SDRAM -#define SH2_TIMER_RUN (1 << 6) // SOC WDT timer is running #define SH2_IN_DRC (1 << 7) // DRC in use unsigned int state; uint32_t poll_addr; diff --git a/pico/32x/32x.c b/pico/32x/32x.c index 2b054c5c..dcbd1e90 100644 --- a/pico/32x/32x.c +++ b/pico/32x/32x.c @@ -123,7 +123,6 @@ void Pico32xStartup(void) PicoMemSetup32x(); p32x_pwm_ctl_changed(); - p32x_timers_recalc(); Pico32x.regs[0] |= P32XS_ADEN; @@ -246,8 +245,6 @@ void PicoReset32x(void) p32x_m68k_poll_event(0, -1); p32x_sh2_poll_event(msh2.poll_addr, &msh2, SH2_IDLE_STATES, Pico.t.m68c_aim); p32x_sh2_poll_event(ssh2.poll_addr, &ssh2, SH2_IDLE_STATES, Pico.t.m68c_aim); - p32x_pwm_ctl_changed(); - p32x_timers_recalc(); } } @@ -397,6 +394,16 @@ static void vint_event(unsigned int now) p32x_trigger_irq(NULL, now, P32XI_VINT); } +static void mtimer_event(unsigned int now) +{ + p32x_timer_irq(&msh2, now); +} + +static void stimer_event(unsigned int now) +{ + p32x_timer_irq(&ssh2, now); +} + typedef void (event_cb)(unsigned int now); /* times are in m68k (7.6MHz) cycles */ @@ -407,6 +414,8 @@ static event_cb *p32x_event_cbs[P32X_EVENT_COUNT] = { fillend_event, // P32X_EVENT_FILLEND hint_event, // P32X_EVENT_HINT vint_event, // P32X_EVENT_VINT + mtimer_event, // P32X_EVENT_MTIMER + stimer_event, // P32X_EVENT_STIMER }; // schedule event at some time 'after', in m68k clocks @@ -541,7 +550,7 @@ void p32x_sync_other_sh2(SH2 *sh2, unsigned int m68k_target) /* most timing is in 68k clock */ void sync_sh2s_normal(unsigned int m68k_target) { - unsigned int now, target, next, timer_cycles; + unsigned int now, target, next; int cycles; elprintf(EL_32X, "sh2 sync to %u", m68k_target); @@ -554,7 +563,6 @@ void sync_sh2s_normal(unsigned int m68k_target) now = msh2.m68krcycles_done; if (CYCLES_GT(now, ssh2.m68krcycles_done)) now = ssh2.m68krcycles_done; - timer_cycles = now; pprof_start(m68k); while (CYCLES_GT(m68k_target, now)) @@ -611,20 +619,7 @@ void sync_sh2s_normal(unsigned int m68k_target) if (!(ssh2.state & SH2_IDLE_STATES)) now = ssh2.m68krcycles_done; } - if (CYCLES_GT(now, timer_cycles+STEP_N)) { - if (msh2.state & SH2_TIMER_RUN) - p32x_timer_do(&msh2, now - timer_cycles); - if (ssh2.state & SH2_TIMER_RUN) - p32x_timer_do(&ssh2, now - timer_cycles); - timer_cycles = now; - } } - - if (msh2.state & SH2_TIMER_RUN) - p32x_timer_do(&msh2, now - timer_cycles); - if (ssh2.state & SH2_TIMER_RUN) - p32x_timer_do(&ssh2, now - timer_cycles); - timer_cycles = now; } pprof_end_sub(m68k); @@ -637,9 +632,6 @@ void sync_sh2s_normal(unsigned int m68k_target) if (CYCLES_GT(m68k_target, ssh2.m68krcycles_done)) ssh2.m68krcycles_done = m68k_target; } - - // everyone is in sync now - Pico32x.comm_dirty = 0; } void sync_sh2s_lockstep(unsigned int m68k_target) @@ -684,6 +676,9 @@ void PicoFrame32x(void) Pico.est.rendstatus |= PDRAW_SYNC_NEEDED; PicoFrameHints(); + p32x_timer_do(&msh2, Pico.t.m68c_aim); + p32x_timer_do(&ssh2, Pico.t.m68c_aim); + elprintf(EL_32X, "poll: %02x %02x %02x", Pico32x.emu_flags & 3, msh2.state, ssh2.state); } @@ -715,7 +710,7 @@ void Pico32xStateLoaded(int is_early) CYCLES_GE(sh2s[1].m68krcycles_done - Pico.t.m68c_aim, 500)) sh2s[0].m68krcycles_done = sh2s[1].m68krcycles_done = Pico.t.m68c_aim; p32x_update_irls(NULL, Pico.t.m68c_aim); - p32x_timers_recalc(); + sh2_peripheral_state_loaded(); p32x_pwm_state_loaded(); p32x_run_events(Pico.t.m68c_aim); diff --git a/pico/32x/sh2soc.c b/pico/32x/sh2soc.c index bc0db3eb..902e947e 100644 --- a/pico/32x/sh2soc.c +++ b/pico/32x/sh2soc.c @@ -189,53 +189,74 @@ static void dmac_trigger(SH2 *sh2, struct dma_chan *chan) chan->sar, chan->dar, chan->tcr, chan->chcr, sh2->pc); } -// FIXME, timer state, in unused peri_regs space to have it in state save/load -#define timer_cycles(sh2) *((u32 *)&(sh2)->peri_regs[0x1c]) +// wdt timers for the 2 SH2's + static u32 timer_tick_shift[2]; +static u32 timer_last_cycle[2]; -// timers -void p32x_timers_recalc(void) +void p32x_timer_recalc(SH2 *sh2) { - int tmp, i; + int tmp, i = sh2->is_slave; + + if (PREG8(sh2->peri_regs, 0x80) & 0x20) { // TME + // SH2 timer step + tmp = PREG8(sh2->peri_regs, 0x80) & 7; - // SH2 timer step - for (i = 0; i < 2; i++) { - sh2s[i].state &= ~SH2_TIMER_RUN; - if (PREG8(sh2s[i].peri_regs, 0x80) & 0x20) // TME - sh2s[i].state |= SH2_TIMER_RUN; - tmp = PREG8(sh2s[i].peri_regs, 0x80) & 7; // Sclk cycles per timer tick if (tmp >= 6) tmp++; if (tmp) tmp += 5; else tmp = 1; timer_tick_shift[i] = tmp; elprintf(EL_32XP, "WDT cycles[%d] = %d", i, 1 << tmp); + + tmp = (1 << timer_tick_shift[i]) - Pico32x.wdt_cycle[i]; // current tick + if (tmp < 0) tmp = 0; // in case of invalid tick cycle data + tmp += (255 - PREG8(sh2->peri_regs, 0x81)) << timer_tick_shift[i]; + p32x_event_schedule_sh2(sh2, P32X_EVENT_MTIMER+i, DIVQ32(tmp,3)); } } -NOINLINE void p32x_timer_do(SH2 *sh2, unsigned int m68k_slice) +void p32x_timer_do(SH2 *sh2, unsigned int now) { - unsigned int cycles = m68k_slice * 3; + int i = sh2->is_slave; void *pregs = sh2->peri_regs; - int cnt; int i = sh2->is_slave; // WDT timer - timer_cycles(sh2) += cycles; - cnt = (timer_cycles(sh2) >> timer_tick_shift[i]); - if (cnt) { - timer_cycles(sh2) -= cnt << timer_tick_shift[i]; + if (PREG8(sh2->peri_regs, 0x80) & 0x20) { // TME + unsigned int cycles = (now - timer_last_cycle[i]) * 3; + int cnt; - cnt += PREG8(pregs, 0x81); - if (cnt >= 0x100) { - int level = PREG8(pregs, 0xe3) >> 4; - int vector = PREG8(pregs, 0xe4) & 0x7f; - PREG8(pregs, 0x80) |= 0x80; // WOVF - elprintf(EL_32XP, "%csh2 WDT irq (%d, %d)", - i ? 's' : 'm', level, vector); - sh2_internal_irq(sh2, level, vector); - cnt &= 0xff; - } - PREG8(pregs, 0x81) = cnt; + cycles += Pico32x.wdt_cycle[i]; + cnt = (cycles >> timer_tick_shift[i]); + cycles -= cnt << timer_tick_shift[i]; + + PREG8(pregs, 0x81) += cnt; + Pico32x.wdt_cycle[i] = cycles; + timer_last_cycle[i] = now; + } +} + +void p32x_timer_irq(SH2 *sh2, unsigned int now) +{ + void *pregs = sh2->peri_regs; + + if (PREG8(sh2->peri_regs, 0x80) & 0x20) { // TME + int i = sh2->is_slave; + int after = 256 << timer_tick_shift[i]; + int level = PREG8(pregs, 0xe3) >> 4; + int vector = PREG8(pregs, 0xe4) & 0x7f; + + PREG8(pregs, 0x80) |= 0x80; // WOVF + elprintf(EL_32XP, "%csh2 WDT irq (%d, %d)", + sh2->is_slave ? 's' : 'm', level, vector); + + sh2_internal_irq(sh2, level, vector); + p32x_event_schedule_sh2(sh2, P32X_EVENT_MTIMER+i, DIVQ32(after,3)); + + // reset counting to avoid rounding errors + PREG8(pregs, 0x81) = 0x00; + Pico32x.wdt_cycle[i] = 0; + timer_last_cycle[i] = now; } } @@ -249,6 +270,13 @@ void sh2_peripheral_reset(SH2 *sh2) PREG8(sh2->peri_regs, 0x017) = 0xe0; // TOCR } +void sh2_peripheral_state_loaded(void) +{ + timer_last_cycle[0] = timer_last_cycle[1] = Pico.t.m68c_aim; + p32x_timer_recalc(&msh2); + p32x_timer_recalc(&ssh2); +} + // ------------------------------------------------------------------ // SH2 internal peripheral memhandlers // we keep them in little endian format @@ -260,6 +288,8 @@ u32 REGPARM(2) sh2_peripheral_read8(u32 a, SH2 *sh2) DRC_SAVE_SR(sh2); a &= 0x1ff; + if (a == 0x81) + p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2)); d = PREG8(r, a); elprintf_sh2(sh2, EL_32XP, "peri r8 [%08x] %02x @%06x", @@ -279,6 +309,8 @@ u32 REGPARM(2) sh2_peripheral_read16(u32 a, SH2 *sh2) DRC_SAVE_SR(sh2); a &= 0x1fe; + if (a == 0x80) + p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2)); d = r[MEM_BE2(a / 2)]; elprintf_sh2(sh2, EL_32XP, "peri r16 [%08x] %04x @%06x", @@ -297,6 +329,8 @@ u32 REGPARM(2) sh2_peripheral_read32(u32 a, SH2 *sh2) DRC_SAVE_SR(sh2); a &= 0x1fc; + if (a == 0x80) + p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2)); d = sh2->peri_regs[a / 4]; elprintf_sh2(sh2, EL_32XP, "peri r32 [%08x] %08x @%06x", @@ -405,11 +439,14 @@ void REGPARM(3) sh2_peripheral_write16(u32 a, u32 d, SH2 *sh2) if (a == 0x80) { if ((d & 0xff00) == 0xa500) { // WTCSR PREG8(r, 0x80) = d; - p32x_timers_recalc(); - timer_cycles(sh2) = 0; } - if ((d & 0xff00) == 0x5a00) // WTCNT + if ((d & 0xff00) == 0x5a00) { // WTCNT PREG8(r, 0x81) = d; + } + // reset to avoid rounding problems + Pico32x.wdt_cycle[sh2->is_slave] = 0; + timer_last_cycle[sh2->is_slave] = sh2_cycles_done_m68k(sh2); + p32x_timer_recalc(sh2); } else { r[MEM_BE2(a / 2)] = d; if ((a & 0x1c0) == 0x140) diff --git a/pico/pico_int.h b/pico/pico_int.h index da5f6d69..ceee2b7a 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -656,9 +656,7 @@ struct Pico32x unsigned int pad[4]; unsigned int dmac0_fifo_ptr; unsigned short vdp_fbcr_fake; - unsigned short pad2; - unsigned char comm_dirty; - unsigned char pad3; // was comm_dirty_sh2 + unsigned short wdt_cycle[2]; // wdt clocking (was comm_dirty) unsigned char pwm_irq_cnt; unsigned char pad1; unsigned short pwm_p[2]; // pwm pos in fifo @@ -1059,6 +1057,8 @@ enum p32x_event { P32X_EVENT_FILLEND, P32X_EVENT_HINT, P32X_EVENT_VINT, + P32X_EVENT_MTIMER, + P32X_EVENT_STIMER, P32X_EVENT_COUNT, }; extern unsigned int p32x_event_times[P32X_EVENT_COUNT]; @@ -1137,9 +1137,11 @@ void p32x_pwm_state_loaded(void); // 32x/sh2soc.c void p32x_dreq0_trigger(void); void p32x_dreq1_trigger(void); -void p32x_timers_recalc(void); -void p32x_timer_do(SH2 *sh2, unsigned int m68k_slice); +void p32x_timer_recalc(SH2 *sh2); +void p32x_timer_do(SH2 *sh2, unsigned int now); +void p32x_timer_irq(SH2 *sh2, unsigned int now); void sh2_peripheral_reset(SH2 *sh2); +void sh2_peripheral_state_loaded(void); u32 REGPARM(2) sh2_peripheral_read8(u32 a, SH2 *sh2); u32 REGPARM(2) sh2_peripheral_read16(u32 a, SH2 *sh2); u32 REGPARM(2) sh2_peripheral_read32(u32 a, SH2 *sh2); From a09128f1c6a3abbbb8b9f1ddc9f453cd1274ef18 Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 9 May 2025 00:12:20 +0200 Subject: [PATCH 22/39] 32x, fix irq events in idle state --- pico/32x/32x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pico/32x/32x.c b/pico/32x/32x.c index dcbd1e90..a47daa78 100644 --- a/pico/32x/32x.c +++ b/pico/32x/32x.c @@ -437,6 +437,9 @@ void p32x_event_schedule_sh2(SH2 *sh2, enum p32x_event event, int after) unsigned int now = sh2_cycles_done_m68k(sh2); int left_to_next; + if (sh2->state & SH2_IDLE_STATES) + now = SekCyclesDone(); + p32x_event_schedule(now, event, after); left_to_next = C_M68K_TO_SH2(sh2, (int)(event_time_next - now)); From ad9105ce35147b047b74dc22e7b2c27c33c59480 Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 9 May 2025 11:05:30 +0200 Subject: [PATCH 23/39] 32x, fix state load regression in 2f95b81 --- pico/32x/memory.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pico/32x/memory.c b/pico/32x/memory.c index eac7be0a..82da7585 100644 --- a/pico/32x/memory.c +++ b/pico/32x/memory.c @@ -2539,8 +2539,7 @@ void Pico32xMemStateLoaded(void) ssh2.poll_addr = ssh2.poll_cycles = ssh2.poll_cnt = 0; memset(sh2_poll_fifo, 0, sizeof(sh2_poll_fifo)); - if (Pico32x.emu_flags & P32XF_DRC_ROM_C) - sh2_drc_flush_all(); + sh2_drc_flush_all(); } // vim:shiftwidth=2:ts=2:expandtab From 87e270dd4dbc2ac1c7dab2549c32dce0bbac3056 Mon Sep 17 00:00:00 2001 From: kub Date: Sat, 10 May 2025 09:20:55 +0200 Subject: [PATCH 24/39] 32x, treat internal level 0 irqs as disabled they will never be taken anyway --- pico/32x/sh2soc.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/pico/32x/sh2soc.c b/pico/32x/sh2soc.c index 902e947e..9bba0d02 100644 --- a/pico/32x/sh2soc.c +++ b/pico/32x/sh2soc.c @@ -70,7 +70,8 @@ static void dmac_te_irq(SH2 *sh2, struct dma_chan *chan) dmac->vcrdma0 : dmac->vcrdma1; elprintf(EL_32XP, "dmac irq %d %d", level, vector); - sh2_internal_irq(sh2, level, vector & 0x7f); + if (level) + sh2_internal_irq(sh2, level, vector & 0x7f); } static void dmac_transfer_complete(SH2 *sh2, struct dma_chan *chan) @@ -212,7 +213,9 @@ void p32x_timer_recalc(SH2 *sh2) tmp = (1 << timer_tick_shift[i]) - Pico32x.wdt_cycle[i]; // current tick if (tmp < 0) tmp = 0; // in case of invalid tick cycle data tmp += (255 - PREG8(sh2->peri_regs, 0x81)) << timer_tick_shift[i]; - p32x_event_schedule_sh2(sh2, P32X_EVENT_MTIMER+i, DIVQ32(tmp,3)); + + if (PREG8(sh2->peri_regs, 0xe3) >> 4) + p32x_event_schedule_sh2(sh2, P32X_EVENT_MTIMER+i, DIVQ32(tmp,3)); } } @@ -250,8 +253,10 @@ void p32x_timer_irq(SH2 *sh2, unsigned int now) elprintf(EL_32XP, "%csh2 WDT irq (%d, %d)", sh2->is_slave ? 's' : 'm', level, vector); - sh2_internal_irq(sh2, level, vector); - p32x_event_schedule_sh2(sh2, P32X_EVENT_MTIMER+i, DIVQ32(after,3)); + if (level) { + sh2_internal_irq(sh2, level, vector); + p32x_event_schedule_sh2(sh2, P32X_EVENT_MTIMER+i, DIVQ32(after,3)); + } // reset counting to avoid rounding errors PREG8(pregs, 0x81) = 0x00; @@ -369,7 +374,8 @@ static void sci_trigger(SH2 *sh2, u8 *r) int vector = PREG8(oregs, 0x64) & 0x7f; elprintf_sh2(sh2, EL_32XP, "SCI tx irq (%d, %d)", level, vector); - sh2_internal_irq(sh2, level, vector); + if (level) + sh2_internal_irq(sh2, level, vector); } // TODO: TEIE if (PREG8(oregs, 2) & 0x40) { // RIE - rx irq enabled @@ -377,7 +383,8 @@ static void sci_trigger(SH2 *sh2, u8 *r) int vector = PREG8(oregs, 0x63) & 0x7f; elprintf_sh2(sh2->other_sh2, EL_32XP, "SCI rx irq (%d, %d)", level, vector); - sh2_internal_irq(sh2->other_sh2, level, vector); + if (level) + sh2_internal_irq(sh2->other_sh2, level, vector); } } @@ -418,6 +425,10 @@ void REGPARM(3) sh2_peripheral_write8(u32 a, u32 d, SH2 *sh2) d |= 0xe0; PREG8(r, a) = d; break; + case 0x0e3: + p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2)); + p32x_timer_recalc(sh2); + break; default: if ((a & 0x1c0) == 0x140) p32x_sh2_poll_event(a, sh2, SH2_STATE_CPOLL, SekCyclesDone()); @@ -449,6 +460,10 @@ void REGPARM(3) sh2_peripheral_write16(u32 a, u32 d, SH2 *sh2) p32x_timer_recalc(sh2); } else { r[MEM_BE2(a / 2)] = d; + if (a == 0x0e2) { + p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2)); + p32x_timer_recalc(sh2); + } if ((a & 0x1c0) == 0x140) p32x_sh2_poll_event(a, sh2, SH2_STATE_CPOLL, SekCyclesDone()); } From 984702927a940cc67d364c2db862c830e4d8ffcc Mon Sep 17 00:00:00 2001 From: kub Date: Sun, 11 May 2025 22:53:26 +0200 Subject: [PATCH 25/39] update READMEs thanks to jdgleaver for supplying English improvements for quite some time. --- README.md | 15 ++++++++++++--- platform/base_readme.txt | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3b2f40ab..40673ca4 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,20 @@ the pages can be found in `platform/base_readme.txt`. On all platforms with physical mouse support, PicoDrive can use real mouse input to emulate a Sega Mouse or the Pico Pen. A physical mouse can be operated -in either a captured or uncaptured state, selectable via the `Capture mouse` hotkey. -Mouse mode can be activated by setting `mouse` as the input device for one -of the pads. It depends on the game as to which pad should be used for mouse +in either a captured or uncaptured state, selectable via the `Capture mouse` +hotkey. Mouse mode can be activated by setting `mouse` as the input device for +one of the pads. It depends on the game as to which pad should be used for mouse input. More information is located in `platform/base_readme.txt`. +### Sega Menacer and Light Phaser and Konami Justifier + +PicoDrive can use a physical mouse for light gun emulation on all platforms +with mouse support. This can be activated by selecting an input device of +either `light gun` or `justifier`. The `light gun` setting represents the Sega +Menacer for the Mega Drive or the Light Phaser for the Master System; it may +be attached to either pad input port. The `justifier` is only available on +input 2. Supplementary information is provided in `platform/base_readme.txt`. + ### Sega Pico and SC-3000 Keyboards PicoDrive provides support for the Pico and SC-3000 keyboards. This can be diff --git a/platform/base_readme.txt b/platform/base_readme.txt index 69424c42..c111bd0b 100644 --- a/platform/base_readme.txt +++ b/platform/base_readme.txt @@ -101,6 +101,7 @@ through the D-pad if the screen has been switched to either pages or pad, or by mouse if physical mouse support is activated. +#ifdef GENERIC How to use the mouse -------------------- @@ -120,11 +121,44 @@ The physical mouse buttons are mapped as follows: | Physical Mouse | Sega Mouse | Pico Pen | |:--------------:|:----------:|:-----------:| -| Left Button | B | Pen Button | +| Left Button | B | Pen Button | | Middle Button | START | Red Button | | Right Button | C | Pen Up/Down | +How to use a light gun +---------------------- + +PicoDrive additionally supports light guns on systems where mouse support is +available. It can be activated by selecting `light gun` or `justifier` as the +input device. General mouse operation is covered in the "How to use the mouse" +section. + +The `light gun` setting auto configures an input device based on the currently +emulated platform: a Sega Menacer for the Mega Drive (and derivatives) and a +Light Phaser for the Master System. The `justifier` setting corresponds to a +Konami Justifier. This is only available on Mega Drive based systems, and can +only be configured as input device 2. + +Selecting any light gun as input enables the light gun configuration submenu. +This allows for displaying a cursor to indicate the current aiming position, +as well as the adjustment of an aiming offset to correct for any in-game +displacements. + +The physical mouse buttons are mapped as follows: + +| Physical Mouse | Sega Menacer/ | +| | Light Phaser/ | +| | Konami Justifier | +|:--------------:|:----------------:| +| Left Button | A | +| Middle Button | B | +| Right Button | START | + +The pad button mappings may also be used to access other buttons. + + +#endif How to use keyboard input ------------------------- From ffd12278166ca8703cf28dc9c2ff91240a3f8648 Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 16 May 2025 15:29:53 +0000 Subject: [PATCH 26/39] core, fix pad ports unintialised after loading old state --- pico/memory.c | 8 ++++++++ pico/pico_int.h | 1 + pico/state.c | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/pico/memory.c b/pico/memory.c index 9e523cf3..b53cc256 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -677,6 +677,14 @@ NOINLINE void io_ports_write(u32 a, u32 d) PicoMem.ioports[a] = d; } +void io_ports_reset(void) +{ + int port; + + for (port = 0; port < 3; port++) + padTLLatency[port] = padTHLatency[port] = padTHTimeout[port] = SekCyclesDone(); +} + int io_ports_pack(void *buf, size_t size) { size_t b, i; diff --git a/pico/pico_int.h b/pico/pico_int.h index ceee2b7a..9112a2f7 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -760,6 +760,7 @@ void PicoWrite16_io(u32 a, u32 d); void PicoPortUpdate(void); void PicoPortTrigger(void); u32 PicoReadPad(int i, u32 mask); +void io_ports_reset(void); int io_ports_pack(void *buf, size_t size); void io_ports_unpack(const void *buf, size_t size); extern int port_type[3]; diff --git a/pico/state.c b/pico/state.c index 684610d9..575153f0 100644 --- a/pico/state.c +++ b/pico/state.c @@ -429,7 +429,7 @@ static int state_load(void *file) int len_check; int retval = -1; char header[8]; - int ver, has_32x = 0; + int ver, has_32x = 0, has_iov2 = 0; int len, len_vdp = 0; memset(buff_m68k, 0, sizeof(buff_m68k)); @@ -483,6 +483,13 @@ static int state_load(void *file) case CHUNK_VDP: CHECKED_READ2((len_vdp = len), buff_vdp); break; case CHUNK_IOPORTS: CHECKED_READ_BUFF(PicoMem.ioports); break; + case CHUNK_IOPORTSv2: + CHECKED_READ(len, buf); + io_ports_unpack(buf, len); + has_iov2 = 1; + break; + + case CHUNK_PSG: CHECKED_READ2(28*4, sn76496_regs); break; case CHUNK_YM2413: CHECKED_READ(len, buf); @@ -496,7 +503,6 @@ static int state_load(void *file) break; case CHUNK_FM_TIMERS: CHECKED_READ(len, buf); ym2612_unpack_timers(buf, len); break; case CHUNK_FMv3: CHECKED_READ(len, buf); YM2612PicoStateLoad3(buf, len); break; - case CHUNK_IOPORTSv2: CHECKED_READ(len, buf); io_ports_unpack(buf, len); break; case CHUNK_PICO_PCM: CHECKED_READ(len, buf); @@ -641,6 +647,10 @@ readend: Pico.video.status |= (Pico.video.pending_ints << 2) & SR_F; } + // kludge: if there is no new ports data save, they must be initialised + if (!has_iov2) + io_ports_reset(); + Pico.m.dirtyPal = 1; retval = 0; From 52eec923c70a2f54f53069847bf29040a906c1b6 Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 16 May 2025 15:35:14 +0000 Subject: [PATCH 27/39] ui, update credits --- platform/common/menu_pico.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c index 5b7f2584..93303869 100644 --- a/platform/common/menu_pico.c +++ b/platform/common/menu_pico.c @@ -1431,12 +1431,12 @@ static void draw_frame_credits(void) static const char credits[] = "PicoDrive v" VERSION "\n" - "(c) notaz, 2006-2013; irixxxx, 2018-2024\n\n" + "(c) notaz, 2006-2013; irixxxx, 2018-2025\n\n" "Credits:\n" - "fDave: initial code\n" #ifdef EMU_C68K - " Cyclone 68000 core\n" + "fDave: initial code, Cyclone 68000 core" #else + "fDave: initial code\n" "Stef, Chui: FAME/C 68k core\n" #endif #ifdef _USE_DRZ80 @@ -1450,13 +1450,18 @@ static const char credits[] = #ifdef __GP2X__ "Squidge: mmuhack\n" "Dzz: ARM940 sample\n" +#endif +#if defined __PSP__ || defined __PS2__ + "fjtrujy: PSP and PS2 platform code\n" #endif "\n" "special thanks (for docs, ideas):\n" " Charles MacDonald, Haze,\n" " Stephane Dallongeville,\n" " Lordus, Exophase, Rokas,\n" - " Eke, Nemesis, Tasco Deluxe"; + " Eke, Nemesis, Tasco Deluxe,\n" + "special thanks (for testing):\n" + " DrUm78, jdgleaver, freq_mod"; static void menu_main_draw_status(void) { From f96e8fff5b9c9004354ca7a5684548698a15112c Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 16 May 2025 17:38:27 +0200 Subject: [PATCH 28/39] 32x, improve sh2 synchronisation --- pico/32x/32x.c | 4 +++- pico/32x/memory.c | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pico/32x/32x.c b/pico/32x/32x.c index a47daa78..b19bf264 100644 --- a/pico/32x/32x.c +++ b/pico/32x/32x.c @@ -516,8 +516,10 @@ void p32x_sync_other_sh2(SH2 *sh2, unsigned int m68k_target) int left_to_event; int m68k_cycles; - if (osh2->state & SH2_STATE_RUN) + if (osh2->state & SH2_STATE_RUN) { + sh2_end_run(sh2, 0); return; + } m68k_cycles = m68k_target - osh2->m68krcycles_done; if (m68k_cycles < 200) diff --git a/pico/32x/memory.c b/pico/32x/memory.c index 82da7585..7e5f38a2 100644 --- a/pico/32x/memory.c +++ b/pico/32x/memory.c @@ -850,7 +850,7 @@ static void p32x_sh2reg_write8(u32 a, u32 d, SH2 *sh2) Pico32x.sh2_regs[4 / 2] = d; p32x_sh2_poll_event(a, sh2->other_sh2, SH2_STATE_CPOLL, cycles); if (p32x_sh2_ready(sh2->other_sh2, cycles+8)) - sh2_end_run(sh2, 4); + p32x_sync_other_sh2(sh2, cycles); sh2_poll_write(a & ~1, d, cycles, sh2); } return; @@ -877,7 +877,7 @@ static void p32x_sh2reg_write8(u32 a, u32 d, SH2 *sh2) p32x_m68k_poll_event(a, P32XF_68KCPOLL); p32x_sh2_poll_event(a, sh2->other_sh2, SH2_STATE_CPOLL, cycles); if (p32x_sh2_ready(sh2->other_sh2, cycles+8)) - sh2_end_run(sh2, 0); + p32x_sync_other_sh2(sh2, cycles); sh2_poll_write(a & ~1, r[a / 2], cycles, sh2); } return; @@ -971,7 +971,7 @@ static void p32x_sh2reg_write16(u32 a, u32 d, SH2 *sh2) p32x_m68k_poll_event(a, P32XF_68KCPOLL); p32x_sh2_poll_event(a, sh2->other_sh2, SH2_STATE_CPOLL, cycles); if (p32x_sh2_ready(sh2->other_sh2, cycles+8)) - sh2_end_run(sh2, 0); + p32x_sync_other_sh2(sh2, cycles); sh2_poll_write(a, d, cycles, sh2); } return; @@ -1641,7 +1641,7 @@ static void sh2_sdram_poll(u32 a, u32 d, SH2 *sh2) sh2_poll_write(a, d, cycles, sh2); p32x_sh2_poll_event(a, sh2->other_sh2, SH2_STATE_RPOLL, cycles); if (p32x_sh2_ready(sh2->other_sh2, cycles+8)) - sh2_end_run(sh2, 0); + p32x_sync_other_sh2(sh2, cycles); DRC_RESTORE_SR(sh2); } From 7406f513c51e8699a85056dfaddd3aaee5d599ef Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 16 May 2025 17:39:15 +0200 Subject: [PATCH 29/39] update libpicofe, plus some sdl platform for it --- platform/common/emu.c | 20 ++++++++++---------- platform/common/plat_sdl.c | 2 +- platform/libpicofe | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/platform/common/emu.c b/platform/common/emu.c index ad286568..28823f73 100644 --- a/platform/common/emu.c +++ b/platform/common/emu.c @@ -1328,10 +1328,10 @@ static void run_events_ui(unsigned int which) { if (currentConfig.EmuOpt & EOPT_MOUSE) { grab_mode = !grab_mode; - in_update_analog(0, 2, &mouse_x); - in_update_analog(0, 3, &mouse_y); - in_update_analog(0, 0, &mouse_x); - in_update_analog(0, 1, &mouse_y); + in_update_pointer(0, 2, &mouse_x); + in_update_pointer(0, 3, &mouse_y); + in_update_pointer(0, 0, &mouse_x); + in_update_pointer(0, 1, &mouse_y); emu_status_msg("Mouse capture %s", grab_mode ? "on" : "off"); } else { grab_mode = 0; @@ -1400,22 +1400,22 @@ void emu_update_input(void) // update mouse coordinates if there is an emulated mouse if (currentConfig.EmuOpt & EOPT_MOUSE) { if (!grab_mode) { - in_update_analog(0, 0, &mouse_x); - in_update_analog(0, 1, &mouse_y); + in_update_pointer(0, 0, &mouse_x); + in_update_pointer(0, 1, &mouse_y); // scale mouse coordinates from -1024..1024 to 0..screen_w/h PicoIn.mouse[0] = (mouse_x+1024) * 320/2048; PicoIn.mouse[1] = (mouse_y+1024) * 240/2048; } else { int xrel, yrel; - in_update_analog(0, 2, &xrel); - in_update_analog(0, 3, &yrel); + in_update_pointer(0, 2, &xrel); + in_update_pointer(0, 3, &yrel); mouse_x += xrel, mouse_y += yrel; // scale mouse coordinates from -1024..1024 to 0..screen_w/h PicoIn.mouse[0] = (mouse_x+1024) * 320/2048; PicoIn.mouse[1] = (mouse_y+1024) * 240/2048; } - in_update_analog(0, -1, &i); // get mouse buttons, bit 2-0 = RML + in_update_pointer(0, -1, &i); // get mouse buttons, bit 2-0 = RML pl_actions[0] |= map_pointer_buttons(i, currentConfig.input_dev0); pl_actions[1] |= map_pointer_buttons(i, currentConfig.input_dev1); @@ -1843,7 +1843,7 @@ void emu_loop(void) } if (diff > target_frametime + vsync_delay) { // still too fast - plat_wait_till_us(timestamp + (diff - target_frametime)); + plat_wait_till_us(timestamp + (diff - target_frametime - vsync_delay)); } } diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c index 1cea2445..23cdc908 100644 --- a/platform/common/plat_sdl.c +++ b/platform/common/plat_sdl.c @@ -570,7 +570,7 @@ void plat_init(void) in_probe(); // create an artificial resize event to initialize mouse scaling - SDL_Event ev; + static SDL_Event ev; ev.resize.type = SDL_VIDEORESIZE; ev.resize.w = g_menuscreen_w; ev.resize.h = g_menuscreen_h; diff --git a/platform/libpicofe b/platform/libpicofe index 80612f5f..289762df 160000 --- a/platform/libpicofe +++ b/platform/libpicofe @@ -1 +1 @@ -Subproject commit 80612f5f31a3201e6c7f710109a9c8b48f6bb757 +Subproject commit 289762dff30c6d715e927c46cf9c2916dc54d02e From 25b80647ec6a04d3901a20b791a0dfcceef2cae7 Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Tue, 20 May 2025 22:16:57 +0200 Subject: [PATCH 30/39] Use proper vsync callback definition --- platform/ps2/emu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ps2/emu.c b/platform/ps2/emu.c index b3379cbd..9c8845e6 100644 --- a/platform/ps2/emu.c +++ b/platform/ps2/emu.c @@ -375,7 +375,7 @@ static void sound_deinit(void) #define is_16bit_mode() \ (currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X) || is_bg_frame) -static int vsync_handler(void) +static int vsync_handler(int cause) { iSignalSema(vsync_sema_id); if (sound_stopped) From ceb9b27c22b7823be5aa5fb5c5b4feddad279efb Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 22 May 2025 21:55:25 +0200 Subject: [PATCH 31/39] update changelog --- ChangeLog | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/ChangeLog b/ChangeLog index a8ba44eb..0d35cc7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,217 @@ +2.05 (2025-05-xx) + + added lightgun support for SDL platforms, using the host mouse for Menacer, Justifier, and Light Phaser + * slightly improved SDL window resize handling + * slightly improved UI for keyboard settings + * fixed major VDP window rendering regression (games using a VDP window with color table != 0) + * fixed interlaced mode rendering in ARMv7 assembly + * fixed VDP sprite parsing for MD (fixes a test case in SpriteMaskingTestRom) + * fixed crash in VDP rendering (DMA pointer wraparound) + * fixed 32X VINT timing + * improved 32X interrupt handling (needs work, still not fully as the SH7604 handles it) + * improved 32X WDT handling + * several 32X CPU snychronisation fixes + +2.04 (2025-04-01) + + added mouse support for SDL platforms, using the host mouse for Sega Mouse, Pico Pen + * improved scaling when resizing the window on SDL platforms (Linux, OSX, Windows) + + added preselect window sizes for integer scaling for SDL platforms + + added fullscreen mode for SDL platforms (well, sort of) + * attempted to improve pad detection for ps2 + + noticeably improved save state accuracy (for retroarch run-ahead) + * made the DC filter reacting faster (closer to real MD according to notaz' measurements) + * fixed a crash when using MD VDP hscroll in conjunction with 2-cell mode + * fixed handling shadow/highlight in MD VDP layer A window (fixes Int. Super Star Soccer) + * added toggling the field bit in MD VDP status for interlace modes + + added some support for MD VDP non-linear color DAC (Knuckles Chaotix color test) + * fixed MD+ playing binary tracks as audio (fixes a bug in DOOM Fusion) + * improve MCD CPU sync (fixes Shadow of the Beast) + * fixed 32X handling of MD H32 mode (MD and 32X layer have 4 px offset) + * fixed GG region code reading (fixes Pop Breaker) + * fixed GG screen in 224 and 240 line mode (fixes Micro Machines) + * fixed GG/SMS display off after scanline start (fixes Madou I) + +2.03 (2025-02-15) + + added support for Pico keyboard (thanks @qufb for supplying this, literally years ago. Mea maxima culpa!) + + added support for SC-3000 keyboard and cassette drive + + added configurable mapping for physical keyboard + + added virtual keyboard for devices not having a physical keyboard + * improved window handling + * fix bug in libretro frontend for Pico pad handling (thanks @theLilaQ) + * fixes for PS2 vsync handling (thanks @fjtrujy; this is still not being perfect) + * fix a crash for MD when rendering in 2-cell VSRAM mode (thanks @notaz for pointing this out) + * more fixes for MD+ (for running Doom 32x Resurrection and Fusion) + * fix for 32X screen rendering (for running Sonic Robo Blast 2) + * fix handling of the 32X cycles UI option + * other minor bug fixes and optimizations + +2.02 (2024-12-29) + * fixes for MSU and MD+ (for running Doom 32x Resurrection and Fusion) + + minimal SMS 3D glasses support + * updates to libretro common (apparently fixes some obscure bugs) + * some CD fixes + * fix a cartridge mapping bug for ARM32 (cyclone) + * fixes for 32X watchdog timer handling (for running Sonic Robo Blast 2, thanks @notaz) + * other minor bug fixes + +2.01 (2024-10-17) + + added windows build + + added integer scaling for SDL window mode + + added support for mode 1 + + added support for MD+/32X+ + * improved CD emulation compatibility + + added support for J-cart games (with 2 additional on-cart controller ports) + + added some more mappings for Mega Drive games + + added PS2 support for multitap and up to 4 controllers + * fixes for PS2 and PSP (audio, menu) + * other bug fixes + +2.00 (2024-07-19) + * improved system detection by extension + * improved save/load (Z80, VDP state) + * improved some auto mappings for SMS + + added stereo for YM2612 DAC output + + updated libchdr for zstd support + * fixes for pandora + * changed miyoo build to ipk + * switched miyoo menu key to RESET + + added some more MD cartridges + * added more keys for some opendingux devices + * several fixes for SH2 DRC + * several VDP rendering fixes + * a lot of other bug fixes (32X reset, Z80 timing, etc.) + +2.00-beta3 (2024-04-28) + * bug fixes (VDP reset, Z80 reset) + * improvements for Sega/Mega CD + +2.00-beta2 (2024-03-26) + + substantial overhaul of Sega Pico UI + supports png images for pad overlays and storyware, new hotkeys, new option (see below) + * improved z80 and 68k timing for md and alike + * improved timing for 68k access to z80 bus + * improved 68k open bus read + + implemented "busy" status bit in ym2612 + * fixes for big endian CPUs for state save/load + +2.00-beta1 (2024-03-03) + + added ps2 standalone target (thanks to fjtrujy for the fundamentals) + * improved psp support + * fixed menu RGB/BGR handling + * fixed several Z80 related timing inaccuracies + * slightly increased volume for CD audio + * other bug fixes + +2.00-alpha3 (2024-01-13) + * improved pandora layer handling + + added handling of "fantom bitmap" (DMA to background color) + + added SMS option to select TMS bitmap for legacy gfx modes + + added stereo sound support for GG + + overall revision of Pico support (correct sound, better handling of pen, better save/load handling) + +2.00-alpha2 (2023-12-14) + * bug fixes and improvements for psp, miyoo, opendingux, pandora + + UI fixes and improvements + * other bugfixes + +2.00-alpha1 (2023-11-09) + + improved MD FM sound (DAC ladder effect/clipping, filtering, native rate) + + added support for 32X using MD H32 mode + + added support for MSU-MD (cartridge+audio CD for MD and 32X) + + added basic MD multiplayer support (team player, 4-way play) + + added mapping for some additional Mega Drive games + + added support for both Overdrive demos + * improved save/load handling (SVP, MD mapping, MCD/32X cycle counter) + + added more mappers for SMS cartridges + + added some support for GG cartridges running in SMS mode + * improved SMS graphics if 1st column is blanked + + added missing TMS9918 modes + + added SG-1000/SC-3000 support (no kbd yet, upcoming) + + added additional handhelds/host systems (miyoo, gkd, rg99, odbeta) + * improved handling of files by extension + + revised menu (more consistent options handling, option profiles) + + added dynarec support for PS3 using libretro + * add CI to repo (including artifact uploading for the curious) + * improved psp support + + revived pandora support + * a plethora of bug fixes and minor improvements (also most probably new bugs ;-)) + +1.99 (2021-11-14) + + improved Master System support + + added SRAM save support for SMS cartridges + + added a lot of mappers for SMS cartridges + + added Game Gear support + + added Master System TMS9918 modes 0 and 2 + + added upscaling for improved aspect ratios + * improved Sega/Mega CD support + + added mapping for some additional Mega Drive games + + improvements for libretro support (new menu structure courtesy of jdgleaver) + * a load of bug fixes and minor improvements (also most probably new bugs ;-)) + +1.98 (2021-04-02) + + CHD support for CD images + + mp3 playback support via minimp3 + + big endian host platform support + + sound low pass filtering for more realistic sound (original HW has analog low pass filter) + + menu revision + * improvements for 8bit fast rendererer (mainly better PAL support) + + basic support for legacy dingux (somewhat limited) and retrofw devices + + experimental support for PSP (incomplete) + * a load of bug fixes and minor improvements (also most probably new bugs ;-)) + +1.97 (2020-11-23) + + powerpc support for DRC + + 2x mode in SDL video overlay mode for better color resolution + + FM support for Master System (YM2413) (thanks to @hiroshica for supplying) + * bug fixes + +1.96 (2020-05-16) + + added YM2612 SSG-EG emulation + * optimisations for SH2 DRC + + added a DC filter to 32X PWM sound to reduce clipping + * improved VDP emulation (some Overdrive 2 support) + + completely rewrote VDP FIFO handling for improved timing accuracy + + revived SVP DRC for ARM + +1.95 (2019-11-27) + + added RISC-V code emitter for SH2 DRC + * SH2 DRC optimisations + +1.94 (2019-10-12) + + revive Helix Mp3 decoder support for ARM (Helix sources not supplied due to licensing) + * update GPH device support to use newer GCC 4.7 + * ARM specific optimisations (speedup for emulated memory access, 32X draw code, DRC disassemblers) + * a lot of SH2 DRC and 32X emulation fixes to improve game compatibility + * improved DRC code generation (generated code now noticeably faster) + * improved SH2 DRC memory access functions (added 32 bit versions, sign extension) + * improved SH2 DRC register caching + + added branch caching to SH2 DRC + * optimisations to DRC ARM backend (code reordering, literal pool) + + added polling/idling loop detection and basic loop optimisation to SH2 DRC + + added call/return cache for SH2 DRC + + added cache for communication areas between 32X CPUs + + added T bit caching to ARM SH2 DRC backend (kept in host CPU flags as long as possible if possible) + + added x86_64. MIPS and aarch64 backends to SH2 DRC + * 32X DMA emulation optimisation + + revised SH2 DRC branch handling and host code block linking + + added 32X PWM interrupt optimisation (configurable since it may not work with all games) + * improved auto frameskipping + +1.93 (2019-01-25) + + implemented SSF2 mapper for 32X + + revived x86 DRC + + added 68K overclocking + + improved FAME/C 68K emulation speed + + support for SMS cheats + * 32X DRC bug fixes and improvements + * other bug fixes + +1.92 (2017-10-04) + + some support for obscure VDP features (128K, debug register) + + import SPI EEPROM from GPGX + * fixes for FAME/C 68k emulation + + added Raspberry Pi 1,2 support + + other bug fixes + 1.91 (2013-10-12) + Added OpenDingux support (Paul Cercueil). * Save directory changed to ~/.picodrive/ for generic platform build From daa07a149bed63a9ecb73aff3fcb92cd7d50d441 Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 22 May 2025 22:40:55 +0200 Subject: [PATCH 32/39] ui, fix typo --- platform/common/menu_pico.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c index 93303869..fa0dec98 100644 --- a/platform/common/menu_pico.c +++ b/platform/common/menu_pico.c @@ -1434,7 +1434,7 @@ static const char credits[] = "(c) notaz, 2006-2013; irixxxx, 2018-2025\n\n" "Credits:\n" #ifdef EMU_C68K - "fDave: initial code, Cyclone 68000 core" + "fDave: initial code, Cyclone 68000 core\n" #else "fDave: initial code\n" "Stef, Chui: FAME/C 68k core\n" From 55f6580f18cb03d3748022eb662c6a4779379616 Mon Sep 17 00:00:00 2001 From: kub Date: Wed, 28 May 2025 19:02:48 +0200 Subject: [PATCH 33/39] platform win32, better support for file selection --- platform/common/emu.c | 4 +-- platform/libpicofe | 2 +- platform/win32/plat.c | 84 +++++++++++++++++++++++++++++++++---------- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/platform/common/emu.c b/platform/common/emu.c index 28823f73..1c7c4414 100644 --- a/platform/common/emu.c +++ b/platform/common/emu.c @@ -37,10 +37,8 @@ #ifndef _WIN32 #define PATH_SEP "/" -#define PATH_SEP_C '/' #else #define PATH_SEP "\\" -#define PATH_SEP_C '\\' #endif #define STATUS_MSG_TIMEOUT 2000 @@ -134,7 +132,7 @@ static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext } p = fname + strlen(fname) - 1; - for (; p >= fname && *p != PATH_SEP_C; p--) + for (; p >= fname && *p != *PATH_SEP; p--) ; p++; strncpy(dst + prefix_len, p, dstlen - prefix_len - 1); diff --git a/platform/libpicofe b/platform/libpicofe index 289762df..cd582996 160000 --- a/platform/libpicofe +++ b/platform/libpicofe @@ -1 +1 @@ -Subproject commit 289762dff30c6d715e927c46cf9c2916dc54d02e +Subproject commit cd5829962036453e2c1e1b8eeea3edcbe5130c70 diff --git a/platform/win32/plat.c b/platform/win32/plat.c index a4020af3..26aafa9e 100644 --- a/platform/win32/plat.c +++ b/platform/win32/plat.c @@ -126,31 +126,68 @@ int scandir(const char *dir, struct dirent ***namelist, int (*select)(const stru char path[MAX_PATH]; struct dirent **entries = NULL; size_t count = 0; + int i; - snprintf(path, sizeof(path), "%s\\*", dir); - handle = FindFirstFile(path, &info); - if (handle == INVALID_HANDLE_VALUE) - return -1; + if (dir == NULL || *dir == '\0') { + // on top level give a list of available drives + DWORD drives = GetLogicalDrives(); + if (drives == 0) return -1; - do { - struct dirent *entry = (struct dirent *)malloc(sizeof(struct dirent)); - if (!entry) { - free(entries); - FindClose(handle); - return -1; + entries = malloc(sizeof(*entries) * 26); + if (!entries) return -1; + + for (i = 0; i < 26; ++i) { + int type; + + if (!(drives & (1 << i))) + continue; + + snprintf(path, MAX_PATH, "%c:\\", 'A' + i); + type = GetDriveTypeA(path); + if (type == DRIVE_NO_ROOT_DIR || type == DRIVE_UNKNOWN) + continue; + + struct dirent *entry = (struct dirent *)malloc(sizeof(struct dirent)); + if (!entry) + goto err; + + snprintf(entry->d_name, MAX_PATH, "%c:", 'A' + i); + entry->d_type = DT_DIR; + + if (!select || select(entry)) + entries[count++] = entry; + else + free(entry); } + } else { - strcpy(entry->d_name, info.cFileName); - entry->d_type = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG; + // create a list of objects in the directory + snprintf(path, sizeof(path), "%s\\*", dir); + handle = FindFirstFile(path, &info); + if (handle == INVALID_HANDLE_VALUE) + return -1; - if (!select || select(entry)) { - entries = realloc(entries, (count + 1) * sizeof(struct dirent *)); - entries[count++] = entry; - } else - free(entry); - } while (FindNextFile(handle, &info)); + do { + struct dirent *entry = (struct dirent *)malloc(sizeof(struct dirent)); + if (!entry) { + FindClose(handle); + goto err; + } - FindClose(handle); + strcpy(entry->d_name, info.cFileName); + entry->d_type = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG; + + if (!select || select(entry)) { + struct dirent **_entries = realloc(entries, (count + 1) * sizeof(struct dirent *)); + if (!_entries) goto err; + entries = _entries; + entries[count++] = entry; + } else + free(entry); + } while (FindNextFile(handle, &info)); + + FindClose(handle); + } // Sort entries if a comparison function is provided if (compar) { @@ -159,6 +196,15 @@ int scandir(const char *dir, struct dirent ***namelist, int (*select)(const stru *namelist = entries; return count; + +err: + if (entries) { + while (count > 0) + if (entries[--count]) + free(entries[count]); + free(entries); + } + return -1; } int alphasort(const struct dirent **a, const struct dirent **b) { From 94b8b8e7367f03ac35361d41f5f81eb40e5436f0 Mon Sep 17 00:00:00 2001 From: kub Date: Wed, 28 May 2025 19:03:37 +0200 Subject: [PATCH 34/39] platform, some cleanup --- platform/common/emu.c | 10 +++------- platform/common/plat_sdl.c | 13 ++++++------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/platform/common/emu.c b/platform/common/emu.c index 1c7c4414..a369d037 100644 --- a/platform/common/emu.c +++ b/platform/common/emu.c @@ -1400,21 +1400,17 @@ void emu_update_input(void) if (!grab_mode) { in_update_pointer(0, 0, &mouse_x); in_update_pointer(0, 1, &mouse_y); - // scale mouse coordinates from -1024..1024 to 0..screen_w/h - PicoIn.mouse[0] = (mouse_x+1024) * 320/2048; - PicoIn.mouse[1] = (mouse_y+1024) * 240/2048; } else { int xrel, yrel; in_update_pointer(0, 2, &xrel); in_update_pointer(0, 3, &yrel); mouse_x += xrel, mouse_y += yrel; - // scale mouse coordinates from -1024..1024 to 0..screen_w/h - PicoIn.mouse[0] = (mouse_x+1024) * 320/2048; - PicoIn.mouse[1] = (mouse_y+1024) * 240/2048; } + // scale mouse coordinates from -1024..1024 to 0..screen_w/h + PicoIn.mouse[0] = (mouse_x+1024) * 320/2048; + PicoIn.mouse[1] = (mouse_y+1024) * 240/2048; in_update_pointer(0, -1, &i); // get mouse buttons, bit 2-0 = RML - pl_actions[0] |= map_pointer_buttons(i, currentConfig.input_dev0); pl_actions[1] |= map_pointer_buttons(i, currentConfig.input_dev1); } diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c index 23cdc908..3f4886e5 100644 --- a/platform/common/plat_sdl.c +++ b/platform/common/plat_sdl.c @@ -206,6 +206,12 @@ static int clear_buf_cnt, clear_stat_cnt; static void resize_buffers(void) { + // update pitch as it is needed for resizing, and the menu bg scaler + if (plat_sdl_overlay || plat_sdl_gl_active) + g_menuscreen_pp = g_menuscreen_w; + else + g_menuscreen_pp = plat_sdl_screen->pitch / 2; + // make sure the shadow buffers are big enough in case of resize if (shadow_size < g_menuscreen_pp * g_menuscreen_h * 2) { shadow_size = g_menuscreen_pp * g_menuscreen_h * 2; @@ -316,7 +322,6 @@ void plat_video_flip(void) SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) { plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, -1); SDL_WM_GrabInput(SDL_GRAB_OFF); - g_menuscreen_pp = plat_sdl_screen->pitch/2; resize_buffers(); // force upper layer to use new dimensions @@ -368,12 +373,6 @@ void plat_video_menu_update(void) SDL_WM_GrabInput(SDL_GRAB_OFF); } - // update pitch as it is needed by the menu bg scaler - if (plat_sdl_overlay || plat_sdl_gl_active) - g_menuscreen_pp = g_menuscreen_w; - else - g_menuscreen_pp = plat_sdl_screen->pitch / 2; - resize_buffers(); } From 2ab2508ef7c6475e80a11990ee87bc3ebe55b124 Mon Sep 17 00:00:00 2001 From: kub Date: Wed, 28 May 2025 19:35:24 +0200 Subject: [PATCH 35/39] core, some more divisions by multiplying with inverse --- pico/sms.c | 4 ++-- pico/videoport.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pico/sms.c b/pico/sms.c index c04e5ada..58e27e58 100644 --- a/pico/sms.c +++ b/pico/sms.c @@ -155,8 +155,8 @@ static u8 vdp_hcounter(int cycles) { // 171 slots per scanline of 228 clocks, counted 0xf4-0x93, 0xe9-0xf3 // this matches h counter tables in SMSVDPTest: - // hc = (cycles+2) * 171 /228 -1 + 0xf4; - int hc = (((cycles+2) * ((171<<8)/228))>>8)-1 + 0xf4; // Q8 to avoid dividing + // hc = (cycles+2) * 171 /228 -1 + 0xf4; + int hc = DIVQ32((cycles+2) * 171, 228) -1 + 0xf4; if (hc > 0x193) hc += 0xe9-0x93-1; return hc; } diff --git a/pico/videoport.c b/pico/videoport.c index 7a126d5a..df369ad5 100644 --- a/pico/videoport.c +++ b/pico/videoport.c @@ -1133,10 +1133,10 @@ PICO_INTERNAL_ASM u32 PicoVideoRead(u32 a) // at the start of the line, the latency in cycles already is in c. d = pv->hv_latch & 0xff; if (pv->reg[12]&1) { // H40/H32? - c = c * ((1LL*slots40<<32)/slcpu) >> 32; // slots since line start + c = DIVQ32(c * slots40, slcpu); // slots since line start if (d < gapstart40 && d+c >= gapstart40) d += gapend40-gapstart40; } else { - c = c * ((1LL*slots32<<32)/slcpu) >> 32; + c = DIVQ32(c * slots32, slcpu); if (d < gapstart32 && d+c >= gapstart32) d += gapend32-gapstart32; } d = (pv->hv_latch & 0xff00) | ((d+c) & 0x00ff); From f17b51eeca95271ef2bc07c37b0f0bdd1fab2780 Mon Sep 17 00:00:00 2001 From: kub Date: Mon, 19 May 2025 23:00:01 +0200 Subject: [PATCH 36/39] build, automate gles check for sdl --- .github/workflows/ci.yml | 5 +-- Makefile | 14 ++------ configure | 68 ++++++++++++++++++++++---------------- platform/common/plat_sdl.c | 33 +++++------------- 4 files changed, 54 insertions(+), 66 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e7574a8..519b4715 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -177,7 +177,7 @@ jobs: run: | git config --global --add safe.directory $PWD ver=$(cut -d'"' -f2 platform/common/version.h)-$(git rev-parse --short HEAD) - ./configure --platform=opendingux-gcw0 + ./configure --platform=opendingux-gcw0 --with-sdl-gles=no make -j$(getconf _NPROCESSORS_ONLN) mv PicoDrive.opk PicoDrive-gcw0-$ver.opk - name: artifacts @@ -239,7 +239,8 @@ jobs: run: | git config --global --add safe.directory $PWD ver=$(cut -d'"' -f2 platform/common/version.h)-$(git rev-parse --short HEAD) - ./configure --platform=retrofw + # toolchain has EGL, but retrofw 2.2 has not, so disable it + ./configure --platform=retrofw --with-sdl-gles=no make -j$(getconf _NPROCESSORS_ONLN) mv PicoDrive.opk PicoDrive-retrofw-$ver.opk - name: artifacts diff --git a/Makefile b/Makefile index 5cd200ba..93a31e35 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ endif ifeq "$(ASAN)" "1" CFLAGS += -fsanitize=address -fsanitize=leak -fsanitize=bounds -fno-omit-frame-pointer -fno-common -O1 -g - LDLIBS += -fsanitize=address -fsanitize=leak -fsanitize=bounds -static-libasan + LDFLAGS += -fsanitize=address -fsanitize=leak -fsanitize=bounds -static-libasan else ifeq "$(DEBUG)" "0" CFLAGS += -O3 -DNDEBUG @@ -173,15 +173,7 @@ use_inputmap ?= 1 PLATFORM := generic endif ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","rpi1" "rpi2")) -CFLAGS += -DHAVE_GLES -DRASPBERRY -CFLAGS += -I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads/ -I/opt/vc/include/interface/vmcs_host/linux/ -LDFLAGS += -ldl -lbcm_host -L/opt/vc/lib -# Stupid renaming occured in latest raspbian... -ifneq (,$(wildcard /opt/vc/lib/libbrcmGLESv2.so)) -LDFLAGS += -lbrcmEGL -lbrcmGLESv2 -else -LDFLAGS += -lEGL -lGLESv2 # on raspi GLESv1_CM is included in GLESv2 -endif +CFLAGS += -DHAVE_GLES -DSDL_BUFFER_3X -DSDL_REDRAW_EVT OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME OBJS += platform/common/plat_sdl.o platform/common/inputmap_kbd.o OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o @@ -189,10 +181,8 @@ OBJS += platform/libpicofe/linux/plat.o USE_FRONTEND = 1 endif ifeq "$(PLATFORM)" "generic" -#ifeq (y,$(shell echo "\#include " | $(CC) -E -xc - >/dev/null 2>&1 && echo y)) ifeq "$(HAVE_GLES)" "1" CFLAGS += -DHAVE_GLES -LDFLAGS += -lEGL -lGLESv1_CM endif CFLAGS += -DSDL_OVERLAY_2X -DSDL_BUFFER_3X -DSDL_REDRAW_EVT OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME diff --git a/configure b/configure index d267c26b..5efe139a 100755 --- a/configure +++ b/configure @@ -49,8 +49,7 @@ have_armv7="" have_arm_oabi="" have_arm_neon="" have_libavcodec="" -have_libchdr="" -have_gles="no" +have_gles="" need_sdl="no" need_zlib="no" @@ -78,11 +77,9 @@ set_platform() case "$platform" in rpi1) MFLAGS="-mcpu=arm1176jzf-s -mfpu=vfp" - have_gles="yes" ;; rpi2) MFLAGS="-mcpu=cortex-a7 -mfpu=neon" - have_gles="yes" ;; generic) MFLAGS="" @@ -217,12 +214,6 @@ if ! test -f "platform/libpicofe/README"; then fail "libpicofe is missing, please run 'git submodule update --init'" fi -#if [ "$need_warm" = "yes" ]; then -# if ! test -f "frontend/warm/README"; then -# fail "wARM is missing, please run 'git submodule init && git submodule update'" -# fi -#fi - if [ -z "$ARCH" ]; then ARCH=`$CC $MFLAGS $CFLAGS -dumpmachine | awk -F '-' '{print $1}'` fi @@ -359,13 +350,39 @@ EOF compile_object "$@" } -check_libchdr() +check_gles() { + # check for VideoCore stuff for Raspberry Pi + if [ VIDEOCORE != "no" -a -d /opt/vc/include -a -d /opt/vc/lib ]; then + CFLAGS="$CFLAGS -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux" + LDLIBS="$LDLIBS -L/opt/vc/lib" + if [ -f /opt/vc/lib/libbcm_host.so ]; then + LDLIBS="$LDLIBS -lbcm_host" + fi + VIDEOCORE="yes" + fi + + # check for GLES headers cat > $TMPC < - int main (int argc, char *argv[]) { chd_open("", 0, NULL, NULL); } + #include + #include + int main(void) { + return (int)eglGetDisplay( (EGLNativeDisplayType)0 ); + } EOF - compile_object "$@" + if [ "$VIDEOCORE" = "yes" ] && compile_binary $CFLAGS $LDLIBS -lbrcmEGL -lbrcmGLESv2; then + have_gles="yes" + LDLIBS="$LDLIBS -lbrcmEGL -lbrcmGLESv2" # non-standard names in old raspbian... + elif [ "$VIDEOCORE" = "yes" ] && compile_binary $CFLAGS $LDLIBS -lEGL -lGLESv2; then + have_gles="yes" + LDLIBS="$LDLIBS -lEGL -lGLESv2" # on raspi GLESv1_CM is included in GLESv2 + elif compile_binary $CFLAGS $LDLIBS -lEGL -lGLESv1_CM; then + have_gles="yes" + LDLIBS="$LDLIBS -lEGL -lGLESv1_CM" + elif compile_binary $CFLAGS $LDLIBS -lEGL -lGLES_CM; then + have_gles="yes" + LDLIBS="$LDLIBS -lEGL -lGLES_CM" + fi } test -n "$SDL_CONFIG" || SDL_CONFIG="$(ls $SYSROOT/*bin*/sdl-config 2>/dev/null | grep /bin/sdl-config | head -n 1)" @@ -375,7 +392,7 @@ test -n "$SDL_CONFIG" || SDL_CONFIG="$(ls $SYSROOT/*/*bin*/sdl-config 2>/dev/nul SDLVERSION=sdl && echo $SDL_CONFIG | grep -q sdl2 && SDLVERSION=sdl2 test -n "$SDL_CONFIG" || SDL_CONFIG=false -MAIN_LDLIBS="$LDLIBS -lm" +MAIN_LDLIBS="-lm" check_zlib -lz && MAIN_LDLIBS="$MAIN_LDLIBS -lz" || need_zlib="yes" @@ -393,11 +410,6 @@ y|Y|yes) *) have_libavcodec="no" ;; esac -#if check_libchdr; then -# have_libchdr="yes" -# MAIN_LDLIBS="-lchdr $MAIN_LDLIBS" -#fi - # find what audio support we can compile if [ "x$sound_drivers" = "x" ]; then if check_oss; then sound_drivers="$sound_drivers oss"; fi @@ -423,7 +435,7 @@ if [ "$need_sdl" = "yes" ]; then [ -x "$SDL_CONFIG" ] || \ fail "sdl-config is missing; please install libsdl (libsdl1.2-dev)" CFLAGS="$CFLAGS `$SDL_CONFIG --cflags`" - MAIN_LDLIBS="`$SDL_CONFIG --libs` $MAIN_LDLIBS" + LDLIBS="$LDLIBS `$SDL_CONFIG --libs`" SYSLIBS="$SYSLIBS -ldl" need_dl=yes check_sdl `$SDL_CONFIG --libs` || fail "please install libsdl (libsdl1.2-dev)" @@ -440,6 +452,9 @@ if [ "$need_dl" = "yes" ]; then esac fi +# check for OpenGLES with SDL +[ "$need_sdl" = "yes" -a "$have_gles" != "no" ] && check_gles + if check_option -Wno-unused_result; then CFLAGS="$CFLAGS -Wno-unused-result" fi @@ -448,17 +463,17 @@ fi test "x$have_armv6" != "x" || have_armv6="no" test "x$have_armv7" != "x" || have_armv7="no" test "x$have_libavcodec" != "x" || have_libavcodec="no" -test "x$have_libchdr" != "x" || have_libchdr="no" +test "x$have_gles" != "x" || have_gles="no" echo "architecture $ARCH" echo "platform $platform" echo "sound drivers $sound_drivers" echo "C compiler $CC" echo "C compiler flags $MFLAGS $CFLAGS" -echo "libraries $MAIN_LDLIBS" +echo "libraries $LDLIBS $MAIN_LDLIBS" echo "linker flags $LDFLAGS" echo "libavcodec (mp3) $have_libavcodec" -#echo "libchdr $have_libchdr" +echo "use SDL with GLES $have_gles" # echo "ARMv7 optimizations $have_armv7" echo "# Automatically generated by configure" > $config_mak @@ -474,7 +489,7 @@ echo "LD = $LD" >> $config_mak echo "CFLAGS += $MFLAGS $CFLAGS" >> $config_mak echo "ASFLAGS += $MFLAGS $ASFLAGS" >> $config_mak echo "LDFLAGS += $LDFLAGS" >> $config_mak -echo "LDLIBS += $MAIN_LDLIBS" >> $config_mak +echo "LDLIBS += $LDLIBS $MAIN_LDLIBS" >> $config_mak echo >> $config_mak echo "ARCH = $ARCH" >> $config_mak @@ -486,9 +501,6 @@ fi if [ "$have_libavcodec" = "yes" ]; then echo "HAVE_LIBAVCODEC = 1" >> $config_mak fi -if [ "$have_libchdr" = "yes" ]; then - echo "HAVE_LIBCHDR = 1" >> $config_mak -fi if [ "$need_zlib" = "yes" ]; then echo "PLATFORM_ZLIB = 1" >> $config_mak fi diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c index 3f4886e5..7b21bbec 100644 --- a/platform/common/plat_sdl.c +++ b/platform/common/plat_sdl.c @@ -222,7 +222,8 @@ static void resize_buffers(void) void plat_video_set_size(int w, int h) { - if ((plat_sdl_overlay || plat_sdl_gl_active) && w <= 320 && h <= 240) { + if ((plat_sdl_overlay || plat_sdl_gl_active || !plat_sdl_is_windowed()) && + w <= 320 && h <= 240) { // scale to the window, but mind aspect ratio (scaled to 4:3): // w *= win_aspect / 4:3_aspect or h *= 4:3_aspect / win_aspect if (g_menuscreen_w * 3/4 >= g_menuscreen_h) @@ -241,6 +242,7 @@ void plat_video_set_size(int w, int h) area = (struct area) { g_screen_width,g_screen_height }; plat_sdl_change_video_mode(g_screen_width, g_screen_height, 0); } + plat_sdl_gl_scaling(currentConfig.filter); } if (plat_sdl_overlay || plat_sdl_gl_active) { // use shadow buffer for overlays @@ -370,6 +372,7 @@ void plat_video_menu_update(void) w = g_menuscreen_w, h = g_menuscreen_h; plat_sdl_change_video_mode(w, h, -1); } while (w != g_menuscreen_w || h != g_menuscreen_h); + plat_sdl_gl_scaling(currentConfig.filter); SDL_WM_GrabInput(SDL_GRAB_OFF); } @@ -380,6 +383,7 @@ void plat_video_menu_enter(int is_rom_loaded) { if (SDL_MUSTLOCK(plat_sdl_screen)) SDL_UnlockSurface(plat_sdl_screen); + plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, -1); } void plat_video_menu_begin(void) @@ -432,7 +436,7 @@ void plat_video_loop_prepare(void) // take over any new vout settings area = (struct area) { 0, 0 }; - plat_sdl_change_video_mode(0, 0, 0); +// plat_sdl_change_video_mode(0, 0, 0); // switch over to scaled output if available, but keep the aspect ratio if (plat_sdl_overlay || plat_sdl_gl_active) @@ -468,31 +472,12 @@ static void plat_sdl_resize(int w, int h) { // take over new settings #if defined(__OPENDINGUX__) - if (currentConfig.vscaling != EOPT_SCALE_HW && w == 320 && h == 480) { - g_menuscreen_h = 240; - g_menuscreen_w = 320; - resized = 1; - } else + if (currentConfig.vscaling != EOPT_SCALE_HW && w == 320 && h == 480) + h = 240; #endif if (g_menuscreen_w != (w & ~3) || g_menuscreen_h != (h & ~3)){ g_menuscreen_h = h & ~3; g_menuscreen_w = w & ~3; -#if 0 // auto resizing may be nice, but creates problems on some SDL platforms - if (!plat_sdl_overlay && !plat_sdl_gl_active && - plat_sdl_is_windowed() && !plat_sdl_is_fullscreen()) { - // in SDL window mode, adapt window to integer scaling - if (g_menuscreen_w * 3/4 >= g_menuscreen_h) - g_menuscreen_w = g_menuscreen_h * 4/3; - else - g_menuscreen_h = g_menuscreen_w * 3/4; - g_menuscreen_w = g_menuscreen_w/320*320; - g_menuscreen_h = g_menuscreen_h/240*240; - if (g_menuscreen_w == 0) { - g_menuscreen_w = 320; - g_menuscreen_h = 240; - } - } -#endif resized = 1; } } @@ -534,7 +519,7 @@ void plat_init(void) } plat_sdl_quit_cb = plat_sdl_quit; - plat_sdl_resize_cb = plat_sdl_resize; +// plat_sdl_resize_cb = plat_sdl_resize; SDL_WM_SetCaption("PicoDrive " VERSION, NULL); From 936bfde341c6233ee4f2c2f089de3de00c16bc04 Mon Sep 17 00:00:00 2001 From: kub Date: Thu, 22 May 2025 22:38:56 +0200 Subject: [PATCH 37/39] build+platform, make gl libs loadable at runtime --- .github/workflows/ci.yml | 4 ++-- Makefile | 6 +++--- configure | 9 +++++---- platform/common/emu.h | 1 + platform/common/plat_sdl.c | 16 ++++++++++++---- platform/linux/menu.c | 3 +-- 6 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 519b4715..8fe9c097 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -177,7 +177,7 @@ jobs: run: | git config --global --add safe.directory $PWD ver=$(cut -d'"' -f2 platform/common/version.h)-$(git rev-parse --short HEAD) - ./configure --platform=opendingux-gcw0 --with-sdl-gles=no + ./configure --platform=opendingux-gcw0 make -j$(getconf _NPROCESSORS_ONLN) mv PicoDrive.opk PicoDrive-gcw0-$ver.opk - name: artifacts @@ -240,7 +240,7 @@ jobs: git config --global --add safe.directory $PWD ver=$(cut -d'"' -f2 platform/common/version.h)-$(git rev-parse --short HEAD) # toolchain has EGL, but retrofw 2.2 has not, so disable it - ./configure --platform=retrofw --with-sdl-gles=no + ./configure --platform=retrofw make -j$(getconf _NPROCESSORS_ONLN) mv PicoDrive.opk PicoDrive-retrofw-$ver.opk - name: artifacts diff --git a/Makefile b/Makefile index 93a31e35..129cbae4 100644 --- a/Makefile +++ b/Makefile @@ -173,7 +173,7 @@ use_inputmap ?= 1 PLATFORM := generic endif ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","rpi1" "rpi2")) -CFLAGS += -DHAVE_GLES -DSDL_BUFFER_3X -DSDL_REDRAW_EVT +CFLAGS += -DHAVE_GLES -DEGL_NO_X11 -DSDL_BUFFER_3X -DSDL_REDRAW_EVT OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME OBJS += platform/common/plat_sdl.o platform/common/inputmap_kbd.o OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o @@ -182,7 +182,7 @@ USE_FRONTEND = 1 endif ifeq "$(PLATFORM)" "generic" ifeq "$(HAVE_GLES)" "1" -CFLAGS += -DHAVE_GLES +CFLAGS += -DHAVE_GLES -DEGL_NO_X11 endif CFLAGS += -DSDL_OVERLAY_2X -DSDL_BUFFER_3X -DSDL_REDRAW_EVT OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME @@ -294,7 +294,7 @@ OBJS += platform/common/main.o platform/common/emu.o platform/common/upscale.o \ OBJS += platform/libpicofe/input.o platform/libpicofe/readpng.o \ platform/libpicofe/fonts.o ifneq (,$(filter %HAVE_GLES, $(CFLAGS))) -OBJS += platform/libpicofe/gl.o platform/libpicofe/gl_platform.o +OBJS += platform/libpicofe/gl.o platform/libpicofe/gl_platform.o platform/libpicofe/gl_loader.o endif # libpicofe - sound diff --git a/configure b/configure index 5efe139a..70f4ed15 100755 --- a/configure +++ b/configure @@ -364,6 +364,7 @@ check_gles() # check for GLES headers cat > $TMPC < #include int main(void) { @@ -372,16 +373,16 @@ check_gles() EOF if [ "$VIDEOCORE" = "yes" ] && compile_binary $CFLAGS $LDLIBS -lbrcmEGL -lbrcmGLESv2; then have_gles="yes" - LDLIBS="$LDLIBS -lbrcmEGL -lbrcmGLESv2" # non-standard names in old raspbian... +# LDLIBS="$LDLIBS -lbrcmEGL -lbrcmGLESv2" # non-standard names in old raspbian... elif [ "$VIDEOCORE" = "yes" ] && compile_binary $CFLAGS $LDLIBS -lEGL -lGLESv2; then have_gles="yes" - LDLIBS="$LDLIBS -lEGL -lGLESv2" # on raspi GLESv1_CM is included in GLESv2 +# LDLIBS="$LDLIBS -lEGL -lGLESv2" # on raspi GLESv1_CM is included in GLESv2 elif compile_binary $CFLAGS $LDLIBS -lEGL -lGLESv1_CM; then have_gles="yes" - LDLIBS="$LDLIBS -lEGL -lGLESv1_CM" +# LDLIBS="$LDLIBS -lEGL -lGLESv1_CM" elif compile_binary $CFLAGS $LDLIBS -lEGL -lGLES_CM; then have_gles="yes" - LDLIBS="$LDLIBS -lEGL -lGLES_CM" +# LDLIBS="$LDLIBS -lEGL -lGLES_CM" fi } diff --git a/platform/common/emu.h b/platform/common/emu.h index c5436c9e..84bb987b 100644 --- a/platform/common/emu.h +++ b/platform/common/emu.h @@ -199,6 +199,7 @@ void plat_finish(void); void plat_show_cursor(int on); int plat_grab_cursor(int on); int plat_has_wm(void); +void plat_set_window(int w, int h); /* used before things blocking for a while (these funcs redraw on return) */ void plat_status_msg_busy_first(const char *msg); diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c index 7b21bbec..ffb58a57 100644 --- a/platform/common/plat_sdl.c +++ b/platform/common/plat_sdl.c @@ -70,7 +70,8 @@ int plat_target_init(void) FILE *f = fopen("/proc/device-tree/compatible", "r"); if (f) { char buf[10]; - int c = fread(buf, 1, sizeof(buf), f); + int c = fread(buf, 1, sizeof(buf)-1, f); + buf[c] = '\0'; if (strncmp(buf, "gcw,", 4) == 0) plat_device = "gcw0"; } @@ -322,7 +323,7 @@ void plat_video_flip(void) // take over resized settings for the physical SDL surface if (resized && plat_sdl_is_windowed() && SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) { - plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, -1); + plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 1); SDL_WM_GrabInput(SDL_GRAB_OFF); resize_buffers(); @@ -370,7 +371,7 @@ void plat_video_menu_update(void) int w, h; do { w = g_menuscreen_w, h = g_menuscreen_h; - plat_sdl_change_video_mode(w, h, -1); + plat_sdl_change_video_mode(w, h, 1); } while (w != g_menuscreen_w || h != g_menuscreen_h); plat_sdl_gl_scaling(currentConfig.filter); SDL_WM_GrabInput(SDL_GRAB_OFF); @@ -383,7 +384,7 @@ void plat_video_menu_enter(int is_rom_loaded) { if (SDL_MUSTLOCK(plat_sdl_screen)) SDL_UnlockSurface(plat_sdl_screen); - plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, -1); + plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 1); } void plat_video_menu_begin(void) @@ -468,6 +469,13 @@ int plat_has_wm(void) return plat_sdl_is_windowed(); } +void plat_set_window(int w, int h) +{ + g_menuscreen_w = w; + g_menuscreen_h = h; + resized = 1; +} + static void plat_sdl_resize(int w, int h) { // take over new settings diff --git a/platform/linux/menu.c b/platform/linux/menu.c index 802c1708..98aa2780 100644 --- a/platform/linux/menu.c +++ b/platform/linux/menu.c @@ -29,8 +29,7 @@ static int mh_windowsize(int id, int keys) if (keys & PBTN_LEFT ) scale--; if (scale <= 0) scale = 1; if (scale > 20) scale = 20; - g_menuscreen_w = scale*320; - g_menuscreen_h = scale*240; + plat_set_window(scale*320, scale*240); } return 0; } From 678e9c1f05516841b639ad5fc857159d50f7ce06 Mon Sep 17 00:00:00 2001 From: kub Date: Fri, 30 May 2025 00:50:19 +0200 Subject: [PATCH 38/39] build, fix miyoo packaging --- platform/miyoo/pkg.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/miyoo/pkg.cfg b/platform/miyoo/pkg.cfg index 731d3e0e..5d9a192c 100644 --- a/platform/miyoo/pkg.cfg +++ b/platform/miyoo/pkg.cfg @@ -1,7 +1,7 @@ # NOTES: ## All variable values should enclosed within double quotes: "" ## CONFIGURATION FILE for `gm2xpkg` script version: -PKGVER="0.7" +PKGVER="0.9" # EXEC commands (set to "1" anyone for desired outcome), you can instead use [OPTIONS] of `gm2xpkg`: PACKAGE="" From 19d41087f51fb65078bce0ff8c791ba717e9e9ee Mon Sep 17 00:00:00 2001 From: kub Date: Sat, 31 May 2025 15:45:49 +0200 Subject: [PATCH 39/39] release 2.05 --- ChangeLog | 6 +++++- platform/common/version.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0d35cc7b..8afb018a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,15 @@ -2.05 (2025-05-xx) +2.05 (2025-05-31) + added lightgun support for SDL platforms, using the host mouse for Menacer, Justifier, and Light Phaser + * improved handling of OpenGL (may enable OpenGL video mode on devices with JZ4770 CPUs) + * improved SDL Joystick/Pad support (should now work with what devices are supported by SDL) + * improved support for Windows platform * slightly improved SDL window resize handling * slightly improved UI for keyboard settings * fixed major VDP window rendering regression (games using a VDP window with color table != 0) * fixed interlaced mode rendering in ARMv7 assembly * fixed VDP sprite parsing for MD (fixes a test case in SpriteMaskingTestRom) * fixed crash in VDP rendering (DMA pointer wraparound) + * fixed pads not initialized correctly after state load * fixed 32X VINT timing * improved 32X interrupt handling (needs work, still not fully as the SH7604 handles it) * improved 32X WDT handling diff --git a/platform/common/version.h b/platform/common/version.h index 3810ddd1..58af9db4 100644 --- a/platform/common/version.h +++ b/platform/common/version.h @@ -1 +1 @@ -#define VERSION "2.04" REVISION +#define VERSION "2.05" REVISION