From: notaz Date: Mon, 5 Oct 2009 16:12:45 +0000 (+0000) Subject: the old-new win32 port X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ced8d2b38431e23be1b26457110febbe10d14c7;p=libpicofe.git the old-new win32 port git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@804 be3aeb3a-fb24-0410-a615-afba39da0efa --- diff --git a/common/config.c b/common/config.c index b2f55cc..a6d4f1d 100644 --- a/common/config.c +++ b/common/config.c @@ -21,7 +21,12 @@ static char *mystrip(char *str); #include "emu.h" #include +// always output DOS endlines +#ifdef _WIN32 +#define NL "\n" +#else #define NL "\r\n" +#endif static int seek_sect(FILE *f, const char *section) { diff --git a/common/input.c b/common/input.c index e3cf071..8950535 100644 --- a/common/input.c +++ b/common/input.c @@ -6,6 +6,7 @@ #include "plat.h" #include "../linux/in_evdev.h" #include "../gp2x/in_gp2x.h" +#include "../win32/in_vk.h" typedef struct { @@ -237,6 +238,7 @@ int in_update(int *result) for (i = 0; i < in_dev_count; i++) { in_dev_t *dev = &in_devices[i]; if (dev->probed && dev->binds != NULL) { + // FIXME: this is stupid, make it indirect switch (dev->drv_id) { #ifdef IN_EVDEV case IN_DRVID_EVDEV: @@ -248,6 +250,9 @@ int in_update(int *result) ret |= in_gp2x_update(dev->drv_data, dev->binds, result); break; #endif + case IN_DRVID_VK: + ret |= in_vk_update(dev->drv_data, dev->binds, result); + break; } } } @@ -808,6 +813,7 @@ void in_init(void) #ifdef IN_EVDEV in_evdev_init(&in_drivers[IN_DRVID_EVDEV]); #endif + in_vk_init(&in_drivers[IN_DRVID_VK]); } #if 0 diff --git a/common/input.h b/common/input.h index 92f5414..06b3de5 100644 --- a/common/input.h +++ b/common/input.h @@ -50,6 +50,7 @@ enum { IN_DRVID_UNKNOWN = 0, IN_DRVID_GP2X, IN_DRVID_EVDEV, + IN_DRVID_VK, IN_DRVID_COUNT, }; diff --git a/common/lprintf.h b/common/lprintf.h index 34ec6b7..48b8d57 100644 --- a/common/lprintf.h +++ b/common/lprintf.h @@ -1,2 +1,10 @@ +#ifdef __cplusplus +extern "C" { +#endif + extern void lprintf(const char *fmt, ...); +#ifdef __cplusplus +} +#endif + diff --git a/common/menu.c b/common/menu.c index fa0480b..887e0cd 100644 --- a/common/menu.c +++ b/common/menu.c @@ -23,7 +23,7 @@ #include static char static_buff[64]; -static char menu_error_msg[64] = { 0, }; +char menu_error_msg[64] = { 0, }; static int menu_error_time = 0; #ifndef UIQ3 diff --git a/common/menu.h b/common/menu.h index d3e49b2..5f7db7b 100644 --- a/common/menu.h +++ b/common/menu.h @@ -159,6 +159,9 @@ void menu_plat_setup(int is_wiz); void text_out16(int x, int y, const char *texto, ...); void me_update_msg(const char *msg); +void menu_romload_prepare(const char *rom_name); +void menu_romload_end(void); + void menu_loop(void); int menu_loop_tray(void); diff --git a/common/plat.h b/common/plat.h index e1fc29b..5580dd8 100644 --- a/common/plat.h +++ b/common/plat.h @@ -15,9 +15,6 @@ void pemu_sound_start(void); void pemu_sound_stop(void); void pemu_sound_wait(void); -void menu_romload_prepare(const char *rom_name); -void menu_romload_end(void); - void plat_early_init(void); void plat_init(void); void plat_finish(void); @@ -44,7 +41,7 @@ int plat_is_dir(const char *path); int plat_wait_event(int *fds_hnds, int count, int timeout_ms); void plat_sleep_ms(int ms); -/* timers, to be used for time diff and must refet to the same clock */ +/* timers, to be used for time diff and must refer to the same clock */ unsigned int plat_get_ticks_ms(void); unsigned int plat_get_ticks_us(void); void plat_wait_till_us(unsigned int us); diff --git a/common/posix.h b/common/posix.h index 2c2d98d..33ca96c 100644 --- a/common/posix.h +++ b/common/posix.h @@ -1,11 +1,19 @@ /* define POSIX stuff: dirent, scandir, getcwd, mkdir */ -#if defined(__linux__) +#if defined(__linux__) || defined(__MINGW32__) #include #include #include #include +#ifdef __MINGW32__ +#warning hacks! +#define mkdir(pathname,mode) mkdir(pathname) +#define d_type d_ino +#define DT_REG 0 +#define DT_DIR 0 +#endif + #else #error "must provide posix" diff --git a/win32/Makefile b/win32/Makefile new file mode 100644 index 0000000..5885235 --- /dev/null +++ b/win32/Makefile @@ -0,0 +1,129 @@ +# settings +CROSS=i586-mingw32msvc- + +#use_musashi = 1 +use_fame = 1 +#use_mz80 = 1 + +-include Makefile.local + +CC = $(CROSS)gcc +CXX = $(CROSS)g++ +LD = $(CROSS)ld +STRIP = $(CROSS)strip + +DEFINES = _UNZIP_SUPPORT IN_VK +CFLAGS += -O2 -Wall -falign-functions=2 -ffast-math +CFLAGS += -I../.. -I. -I../../zlib/ -Idirectx/include/ +LDFLAGS += -L. -Ldirectx/lib/ -lgdi32 -lcomdlg32 -lddraw -ldsound -ldxguid + +# frontend +OBJS += main.o plat.o direct.o dsnd.o in_vk.o + +# common +OBJS += platform/common/emu.o platform/common/menu.o \ + platform/common/config.o platform/common/fonts.o platform/common/readpng.o \ + platform/common/input.o + +# Pico +OBJS += pico/area.o pico/cart.o pico/memory.o pico/pico.o pico/sek.o \ + pico/videoport.o pico/draw2.o pico/draw.o pico/z80if.o pico/patch.o \ + pico/mode4.o pico/sms.o pico/misc.o pico/eeprom.o pico/debug.o +# Pico - CD +OBJS += pico/cd/pico.o pico/cd/memory.o pico/cd/sek.o pico/cd/LC89510.o \ + pico/cd/cd_sys.o pico/cd/cd_file.o pico/cd/cue.o pico/cd/gfx_cd.o \ + pico/cd/area.o pico/cd/misc.o pico/cd/pcm.o pico/cd/buffering.o +# Pico - 32X +OBJS += pico/32x/32x.o pico/32x/memory.o pico/32x/draw.o pico/32x/pwm.o +# Pico - Pico +OBJS += pico/pico/pico.o pico/pico/memory.o pico/pico/xpcm.o +# Pico - sound +OBJS += pico/sound/sound.o pico/sound/sn76496.o pico/sound/ym2612.o pico/sound/mix.o +# Pico - carthw +OBJS += pico/carthw/carthw.o pico/carthw/svp/svp.o pico/carthw/svp/memory.o \ + pico/carthw/svp/ssp16.o pico/carthw/svp/compiler.o +# zlib +OBJS += zlib/gzio.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o \ + zlib/deflate.o zlib/crc32.o zlib/adler32.o zlib/zutil.o zlib/compress.o zlib/uncompr.o +# unzip +OBJS += unzip/unzip.o unzip/unzip_stream.o +# CPU cores +ifeq "$(use_musashi)" "1" +DEFINES += EMU_M68K +OBJS += cpu/musashi/m68kops.o cpu/musashi/m68kcpu.o +#OBJS += cpu/musashi/m68kdasm.o +endif +ifeq "$(use_fame)" "1" +DEFINES += EMU_F68K +OBJS += cpu/fame/famec.o +endif +# z80 +ifeq "$(use_mz80)" "1" +DEFINES += _USE_MZ80 +OBJS += cpu/mz80/mz80.o +else +DEFINES += _USE_CZ80 +OBJS += cpu/cz80/cz80.o +endif +# sh2 +OBJS += cpu/sh2mame/sh2pico.o +# misc +ifeq "$(use_fame)" "1" +ifeq "$(use_musashi)" "1" +OBJS += pico/debugCPU.o +endif +endif + +CFLAGS += $(addprefix -D,$(DEFINES)) +CXXFLAGS = $(CFLAGS) + +vpath %.c = ../.. + +DIRS = platform platform/gp2x platform/common pico pico/cd pico/pico pico/sound pico/carthw/svp \ + pico/32x zlib unzip cpu cpu/musashi cpu/fame cpu/mz80 cpu/cz80 cpu/sh2mame + +TARGET = PicoDrive.exe + +all: mkdirs $(TARGET) +clean: tidy + @$(RM) $(TARGET) +tidy: + $(RM) $(OBJS) $(TARGET).map + rm -rf $(DIRS) + +$(TARGET) : $(OBJS) + @echo ">>>" $@ + $(CC) $(CFLAGS) $^ $(LDFLAGS) -lm -lpng -Wl,-Map=$(TARGET).map -o $@ + $(STRIP) $@ + +mkdirs: + @mkdir -p $(DIRS) + +include ../common/revision.mak + +pico/carthw/svp/compiler.o : ../../pico/carthw/svp/gen_arm.c +pico/pico.o pico/cd/pico.o : ../../pico/pico_cmn.c ../../pico/pico_int.h +pico/memory.o pico/cd/memory.o : ../../pico/pico_int.h ../../pico/memory.h + +../../cpu/musashi/m68kops.c : + @make -C ../../cpu/musashi + +cpu/mz80/mz80.o : ../../cpu/mz80/mz80.asm + @echo $@ + @nasm -f elf $< -o $@ + +../../cpu/mz80/mz80.asm : + @make -C ../../cpu/mz80/ + +.c.o: + @echo ">>>" $< + $(CC) $(CFLAGS) -c $< -o $@ +.s.o: + @echo ">>>" $< + $(CC) $(CFLAGS) -c $< -o $@ + + +cpu/fame/famec.o : ../../cpu/fame/famec.c ../../cpu/fame/famec_opcodes.h + @echo ">>>" $< + $(CC) $(CFLAGS) -Wno-unused -c $< -o $@ + diff --git a/win32/direct.cpp b/win32/direct.cpp new file mode 100644 index 0000000..033f87c --- /dev/null +++ b/win32/direct.cpp @@ -0,0 +1,410 @@ +// ddraw +#include +#include "../common/lprintf.h" +#include "direct.h" +#include "main.h" + +#define EmuWidth 320 +#define EmuHeight 240 + +#define RELEASE(x) if (x) x->Release(); x=NULL; +#define LOGFAIL() lprintf("fail: %s %s:%i\n", __FUNCTION__, __FILE__, __LINE__) + +static LPDIRECTDRAW7 m_pDD = NULL; +static LPDIRECTDRAWSURFACE7 m_pddsFrontBuffer = NULL; +static LPDIRECTDRAWSURFACE7 m_pddsBackBuffer = NULL; + +// quick and dirty stuff.. +void DirectExit(void) +{ + RELEASE(m_pddsBackBuffer); + RELEASE(m_pddsFrontBuffer); + RELEASE(m_pDD); +} + +int DirectInit(void) +{ + HRESULT ret; + LPDIRECTDRAWCLIPPER pcClipper = NULL; + DDSURFACEDESC2 ddsd; + + ret = DirectDrawCreateEx(NULL, (VOID**)&m_pDD, IID_IDirectDraw7, NULL); + if (ret) { LOGFAIL(); return 1; } + + // Set cooperative level + ret = m_pDD->SetCooperativeLevel( FrameWnd, DDSCL_NORMAL ); + if (ret) { LOGFAIL(); goto fail; } + + // Create the primary surface + ZeroMemory( &ddsd, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + ret = m_pDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL ); + if (ret) { LOGFAIL(); goto fail; } + + // Create the backbuffer surface + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + ddsd.dwWidth = EmuWidth; + ddsd.dwHeight = EmuHeight; + + ret = m_pDD->CreateSurface( &ddsd, &m_pddsBackBuffer, NULL ); + if (ret) { LOGFAIL(); goto fail; } + + // clipper + ret = m_pDD->CreateClipper( 0, &pcClipper, NULL ); + if (ret) { LOGFAIL(); goto fail; } + + ret = pcClipper->SetHWnd( 0, FrameWnd ); + if (ret) { LOGFAIL(); goto fail; } + + ret = m_pddsFrontBuffer->SetClipper( pcClipper ); + if (ret) { LOGFAIL(); goto fail; } + + RELEASE(pcClipper); + return 0; + +fail: + RELEASE(pcClipper); + DirectExit(); + return 1; +} + +int DirectScreen(const void *emu_screen) +{ + const unsigned short *ps = (const unsigned short *)emu_screen; + DDSURFACEDESC2 sd; + int ret, x, y; + + memset(&sd, 0, sizeof(sd)); + sd.dwSize = sizeof(sd); + ret = m_pddsBackBuffer->Lock(NULL, &sd, DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_WRITEONLY, NULL); + if (ret) { LOGFAIL(); return 1; } + + //lprintf("w: %i h: %i pi: %i pf: %i\n", sd.dwWidth, sd.dwHeight, sd.lPitch, sd.ddpfPixelFormat.dwRGBBitCount); + + if (sd.ddpfPixelFormat.dwRGBBitCount == 32) + { + int *dst = (int *)sd.lpSurface; + for (y = 0; y < EmuHeight; y++) + { + for (x = 0; x < EmuWidth; x++) + { + int s = *ps++; + dst[x] = ((s&0xf800)<<8) | ((s&0x07e0)<<5) | ((s&0x001f)<<3); + } + dst = (int *)((char *)dst + sd.lPitch); + } + } + else if (sd.ddpfPixelFormat.dwRGBBitCount == 24) /* wine uses this for me */ + { + void *dst = sd.lpSurface; + for (y = 0; y < EmuHeight; y++) + { + unsigned char *dst1 = (unsigned char *) dst; + for (x = 0; x < EmuWidth; x++, dst1 += 3) + { + int s = *ps++; + dst1[2] = (s&0xf800)>>8; dst1[1] = (s&0x07e0)>>3; dst1[0] = s<<3; // BGR + } + dst = (void *)((char *)dst + sd.lPitch); + } + } + else if (sd.ddpfPixelFormat.dwRGBBitCount == 16) + { + unsigned short *dst = (unsigned short *)sd.lpSurface; + for (y = 0; y < EmuHeight; y++) + { + memcpy(dst, ps, EmuWidth*2); + ps += EmuWidth; + dst = (unsigned short *)((char *)dst + sd.lPitch); + } + } + else + { + LOGFAIL(); + } + + ret = m_pddsBackBuffer->Unlock(NULL); + if (ret) { LOGFAIL(); return 1; } + return 0; +} + +int DirectClear(unsigned int colour) +{ + int ret = 0; + DDBLTFX ddbltfx; + ZeroMemory( &ddbltfx, sizeof(ddbltfx) ); + ddbltfx.dwSize = sizeof(ddbltfx); + ddbltfx.dwFillColor = colour; + + if (m_pddsBackBuffer != NULL) + ret = m_pddsBackBuffer->Blt( NULL, NULL, NULL, DDBLT_COLORFILL, &ddbltfx ); + if (ret) { LOGFAIL(); return 1; } + return 0; +} + +int DirectPresent(void) +{ + int ret = 0; + if (FrameRectMy.right - FrameRectMy.left > 0 && FrameRectMy.bottom - FrameRectMy.top > 0) + ret = m_pddsFrontBuffer->Blt(&FrameRectMy, m_pddsBackBuffer, &EmuScreenRect, DDBLT_WAIT, NULL); + if (ret) { LOGFAIL(); return 1; } + return 0; +} + + +/* D3D */ +#ifdef USE_D3D +static IDirect3D8 *Direct3D=NULL; +IDirect3DDevice8 *Device=NULL; +IDirect3DSurface8 *DirectBack=NULL; // Back Buffer + +static IDirect3DVertexBuffer8 *VertexBuffer=NULL; + +struct CustomVertex +{ + float x,y,z; // Vertex cordinates + unsigned int colour; + float u,v; // Texture coordinates +}; +#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) + +static CustomVertex VertexList[4]; + +int DirectInit() +{ + D3DPRESENT_PARAMETERS d3dpp; + D3DDISPLAYMODE mode; + int i,u,ret=0; + + memset(&d3dpp,0,sizeof(d3dpp)); + memset(&mode,0,sizeof(mode)); + + Direct3D=Direct3DCreate8(D3D_SDK_VERSION); if (Direct3D==NULL) return 1; + + // Set up the structure used to create the D3D device: + d3dpp.BackBufferWidth =MainWidth; + d3dpp.BackBufferHeight=MainHeight; + d3dpp.BackBufferCount =1; + d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD; + d3dpp.MultiSampleType =D3DMULTISAMPLE_NONE; + +#ifdef _XBOX + d3dpp.BackBufferFormat=D3DFMT_X8R8G8B8; + d3dpp.FullScreen_RefreshRateInHz=60; +#else + Direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&mode); + d3dpp.BackBufferFormat=mode.Format; + d3dpp.Windowed=1; + d3dpp.hDeviceWindow=FrameWnd; +#endif + + // Try to create a device with hardware vertex processing: + for (i=0;i<3;i++) + { + int behave=D3DCREATE_HARDWARE_VERTEXPROCESSING; + + // Try software vertex processing: + if (i==1) behave=D3DCREATE_MIXED_VERTEXPROCESSING; + if (i==2) behave=D3DCREATE_SOFTWARE_VERTEXPROCESSING; + + Direct3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,FrameWnd, + behave|D3DCREATE_MULTITHREADED,&d3dpp,&Device); + if (Device) break; + } + + if (Device==NULL) + { +#if 0 + // try ref + Direct3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_REF,FrameWnd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED,&d3dpp,&Device); + if (Device==NULL) goto fail0; + HMODULE test = LoadLibrary("d3d8d.dll"); + if (test != NULL) FreeLibrary(test); + else { + error("Sorry, but this program requires Direct3D with hardware acceleration.\n\n" + "You can try using Direct3D software emulation, but you have to install " + "DirectX SDK for it to work\n(it seems to be missing now)."); + goto fail1; + } +#else + goto fail1; +#endif + } + + Device->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&DirectBack); + if (DirectBack==NULL) goto fail1; + + Device->CreateVertexBuffer(sizeof(VertexList),0,D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT,&VertexBuffer); + if (VertexBuffer==NULL) goto fail2; + + ret=TexScreenInit(); if (ret) goto fail3; + + //FontInit(); + + Device->SetRenderState(D3DRS_LIGHTING,0); // Turn off lighting + + // Set up texture modes: + Device->SetTextureStageState(0,D3DTSS_ADDRESSU,D3DTADDRESS_CLAMP); + Device->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP); + + return 0; + +fail3: + RELEASE(VertexBuffer) +fail2: + RELEASE(DirectBack) +fail1: + RELEASE(Device) +fail0: + RELEASE(Direct3D) + + return 1; +} + +void DirectExit() +{ + TexScreenExit(); + + // d3d + RELEASE(VertexBuffer) + RELEASE(DirectBack) + RELEASE(Device) + RELEASE(Direct3D) +} + +int DirectClear(unsigned int colour) +{ + if (Device != NULL) { + Device->Clear(0,NULL,D3DCLEAR_TARGET,colour,1.0f,0); + return 0; + } + + return 1; +} + +int DirectPresent() +{ + if (Device != NULL) { + Device->Present(NULL,NULL,NULL,NULL); + return 0; + } + + return 1; +} + +#define PI 3.14159265f + +static int MakeVertexList() +{ + struct CustomVertex *vert=NULL,*pv=NULL; + float dist=0.0f; + float scalex=0.0f,scaley=0.0f; + unsigned int colour=0xffffff; + float right=0.0f,bottom=0.0f; + + if (LoopMode!=8) colour=0x102040; + + dist=10.0f; scalex=dist*1.3333f; scaley=dist; + + scalex*=640.0f/(float)MainWidth; + scaley*=448.0f/(float)MainHeight; + + vert=VertexList; + + // Put the vertices for the corners of the screen: + pv=vert; + pv->z=dist; + pv->x=-scalex; pv->y=scaley; + pv->colour=colour; pv++; + + *pv=vert[0]; pv->x= scalex; pv->y= scaley; pv++; + *pv=vert[0]; pv->x=-scalex; pv->y=-scaley; pv++; + *pv=vert[0]; pv->x= scalex; pv->y=-scaley; pv++; + + // Find where the screen images ends on the texture + right =(float)EmuWidth /(float)TexWidth; + bottom=(float)EmuHeight/(float)TexHeight; + + // Write texture coordinates: + pv=vert; + pv->u=0.0f; pv->v=0.00f; pv++; + pv->u=right; pv->v=0.00f; pv++; + pv->u=0.0f; pv->v=bottom; pv++; + pv->u=right; pv->v=bottom; pv++; + + return 0; +} + +static int SetupMatrices() +{ + D3DXVECTOR3 eye ( 0.0f, 0.0f, 0.0f ); + D3DXVECTOR3 look( 0.0f, 0.0f, 0.0f ); + D3DXVECTOR3 up ( 0.0f, 1.0f, 0.0f ); + D3DXMATRIX mat; + float nudgex=0.0f,nudgey=0.0f; + + memset(&mat,0,sizeof(mat)); + + mat.m[0][0]=mat.m[1][1]=mat.m[2][2]=mat.m[3][3]=1.0f; + Device->SetTransform(D3DTS_WORLD,&mat); + + look.x=(float)Inp.axis[2]/2457.6f; + look.y=(float)Inp.axis[3]/2457.6f; + look.z=10.0f; + + // Nudge pixels to the centre of each screen pixel: + nudgex=13.3333f/(float)(MainWidth <<1); + nudgey=10.0000f/(float)(MainHeight<<1); + eye.x +=nudgex; eye.y +=nudgey; + look.x+=nudgex; look.y+=nudgey; + + D3DXMatrixLookAtLH(&mat,&eye,&look,&up); + Device->SetTransform(D3DTS_VIEW,&mat); + + D3DXMatrixPerspectiveFovLH(&mat, 0.5f*PI, 1.3333f, 0.2f, 1000.0f); + Device->SetTransform(D3DTS_PROJECTION,&mat); + return 0; +} + +int DirectScreen() +{ + unsigned char *lock=NULL; + int ret; + + if (Device == NULL) + return DirectScreenDDraw(); + + // Copy the screen to the screen texture: +#ifdef _XBOX + TexScreenSwizzle(); +#else + ret=TexScreenLinear(); + if (ret) lprintf("TexScreenLinear failed\n"); +#endif + + SetupMatrices(); + + MakeVertexList(); + + // Copy vertices in: + VertexBuffer->Lock(0,sizeof(VertexList),&lock,0); + if (lock==NULL) { lprintf("VertexBuffer->Lock failed\n"); return 1; } + memcpy(lock,VertexList,sizeof(VertexList)); + VertexBuffer->Unlock(); + + Device->BeginScene(); + Device->SetTexture(0,TexScreen); + Device->SetStreamSource(0,VertexBuffer,sizeof(CustomVertex)); + Device->SetVertexShader(D3DFVF_CUSTOMVERTEX); + Device->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2); + Device->EndScene(); + + return 0; +} +#endif + diff --git a/win32/direct.h b/win32/direct.h new file mode 100644 index 0000000..a064b3f --- /dev/null +++ b/win32/direct.h @@ -0,0 +1,15 @@ +#ifdef __cplusplus +extern "C" { +#endif + +int DirectInit(void); +void DirectExit(void); + +int DirectScreen(const void *emu_screen); +int DirectClear(unsigned int colour); +int DirectPresent(void); + + +#ifdef __cplusplus +} +#endif diff --git a/win32/dsnd.cpp b/win32/dsnd.cpp new file mode 100644 index 0000000..4e1930e --- /dev/null +++ b/win32/dsnd.cpp @@ -0,0 +1,165 @@ +//#pragma warning (disable:4201) +#include +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include "dsnd.h" +#include "../common/lprintf.h" + +#define NSEGS 4 +#define RELEASE(x) if (x) x->Release(); x=NULL; + +static LPDIRECTSOUND DSound; +static LPDIRECTSOUNDBUFFER LoopBuffer; +static LPDIRECTSOUNDNOTIFY DSoundNotify; +static HANDLE seg_played_event; +static int LoopLen, LoopWrite, LoopSeg; // bytes + +static int LoopBlank(void) +{ + void *mema=NULL,*memb=NULL; + DWORD sizea=0,sizeb=0; + + LoopBuffer->Lock(0, LoopLen, &mema,&sizea, &memb,&sizeb, 0); + + if (mema) memset(mema,0,sizea); + + LoopBuffer->Unlock(mema,sizea, memb,sizeb); + + return 0; +} + +int DSoundInit(HWND wnd_coop, int rate, int stereo, int seg_samples) +{ + DSBUFFERDESC dsbd; + WAVEFORMATEX wfx; + DSBPOSITIONNOTIFY notifies[NSEGS]; + int i; + + memset(&dsbd,0,sizeof(dsbd)); + memset(&wfx,0,sizeof(wfx)); + + // Make wave format: + wfx.wFormatTag=WAVE_FORMAT_PCM; + wfx.nChannels=stereo ? 2 : 1; + wfx.nSamplesPerSec=rate; + wfx.wBitsPerSample=16; + + wfx.nBlockAlign=(WORD)((wfx.nChannels*wfx.wBitsPerSample)>>3); + wfx.nAvgBytesPerSec=wfx.nBlockAlign*wfx.nSamplesPerSec; + + // Create the DirectSound interface: + DirectSoundCreate(NULL,&DSound,NULL); + if (DSound==NULL) return 1; + + LoopSeg = seg_samples * 2; + if (stereo) + LoopSeg *= 2; + + LoopLen = LoopSeg * NSEGS; + + DSound->SetCooperativeLevel(wnd_coop, DSSCL_PRIORITY); + dsbd.dwFlags=DSBCAPS_GLOBALFOCUS; // Play in background + dsbd.dwFlags|=DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY; + + // Create the looping buffer: + dsbd.dwSize=sizeof(dsbd); + dsbd.dwBufferBytes=LoopLen; + dsbd.lpwfxFormat=&wfx; + + DSound->CreateSoundBuffer(&dsbd,&LoopBuffer,NULL); + if (LoopBuffer==NULL) return 1; + + LoopBuffer->QueryInterface(IID_IDirectSoundNotify, (LPVOID*)&DSoundNotify); + if (DSoundNotify == NULL) { + lprintf("QueryInterface(IID_IDirectSoundNotify) failed\n"); + goto out; + } + + seg_played_event = CreateEvent(NULL, 0, 0, NULL); + if (seg_played_event == NULL) + goto out; + + for (i = 0; i < NSEGS; i++) { + notifies[i].dwOffset = i * LoopSeg; + notifies[i].hEventNotify = seg_played_event; + } + i = DSoundNotify->SetNotificationPositions(NSEGS, notifies); + if (i != DS_OK) { + lprintf("SetNotificationPositions failed\n"); + goto out; + } + +out: + LoopBlank(); + LoopBuffer->Play(0, 0, DSBPLAY_LOOPING); + return 0; +} + +void DSoundExit(void) +{ + if (LoopBuffer) + LoopBuffer->Stop(); + RELEASE(DSoundNotify); + RELEASE(LoopBuffer) + RELEASE(DSound) + CloseHandle(seg_played_event); + seg_played_event = NULL; +} + +static int WriteSeg(const void *buff) +{ + void *mema=NULL,*memb=NULL; + DWORD sizea=0,sizeb=0; + int ret; + + // Lock the segment at 'LoopWrite' and copy the next segment in + ret = LoopBuffer->Lock(LoopWrite, LoopSeg, &mema, &sizea, &memb, &sizeb, 0); + if (ret != DS_OK) + lprintf("LoopBuffer->Lock() failed: %i\n", ret); + + if (mema) memcpy(mema,buff,sizea); +// if (memb) memcpy(memb,DSoundNext+sizea,sizeb); + if (sizeb != 0) lprintf("sizeb is not 0! (%i)\n", sizeb); + + ret = LoopBuffer->Unlock(mema,sizea, memb, sizeb); + if (ret != DS_OK) + lprintf("LoopBuffer->Unlock() failed: %i\n", ret); + + return 0; +} + +int DSoundUpdate(const void *buff, int blocking) +{ + DWORD play = 0; + int pos; + + LoopBuffer->GetCurrentPosition(&play, NULL); + pos = play; + + // 'LoopWrite' is the next seg in the loop that we want to write + // First check that the sound 'play' pointer has moved out of it: + if (blocking) { + while (LoopWrite <= pos && pos < LoopWrite + LoopSeg) { + WaitForSingleObject(seg_played_event, 5000); + LoopBuffer->GetCurrentPosition(&play, NULL); + pos = play; + } + } + else { + if (LoopWrite <= pos && pos < LoopWrite + LoopSeg) + return 1; + } + + WriteSeg(buff); + + // Advance LoopWrite to next seg: + LoopWrite += LoopSeg; + if (LoopWrite + LoopSeg > LoopLen) + LoopWrite = 0; + + return 0; +} + diff --git a/win32/dsnd.h b/win32/dsnd.h new file mode 100644 index 0000000..9132bcb --- /dev/null +++ b/win32/dsnd.h @@ -0,0 +1,14 @@ +#ifdef __cplusplus +extern "C" { +#endif + +int DSoundInit(HWND wnd_coop, int rate, int stereo, int seg_samples); +void DSoundExit(void); +int DSoundUpdate(const void *buff, int blocking); + +extern short *DSoundNext; + +#ifdef __cplusplus +} +#endif + diff --git a/win32/in_vk.c b/win32/in_vk.c new file mode 100644 index 0000000..625268a --- /dev/null +++ b/win32/in_vk.c @@ -0,0 +1,273 @@ +#define RC_INVOKED // we only need defines +#include +#undef RC_INVOKED +#include + +#include "../common/input.h" +#include "../common/emu.h" // array_size +#include "in_vk.h" + +#define IN_VK_PREFIX "vk:" +#define IN_VK_NKEYS 0x100 + +static const char * const in_vk_prefix = IN_VK_PREFIX; +static const char * const in_vk_keys[IN_VK_NKEYS] = { + [0 ... (IN_VK_NKEYS - 1)] = NULL, + [VK_LBUTTON] = "LBUTTON", [VK_RBUTTON] = "RBUTTON", + [VK_TAB] = "TAB", [VK_RETURN] = "RETURN", + [VK_SHIFT] = "SHIFT", [VK_CONTROL] = "CONTROL", + [VK_LEFT] = "LEFT", [VK_UP] = "UP", + [VK_RIGHT] = "RIGHT", [VK_DOWN] = "DOWN", + [VK_SPACE] = "SPACE", +}; + +// additional player12 keys +int in_vk_add_pl12; + +// up to 4, keyboards rarely allow more +static int in_vk_keys_down[4]; + +/* +#define VK_END 35 +#define VK_HOME 36 +#define VK_INSERT 45 +#define VK_DELETE 46 +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +*/ + +static void in_vk_probe(void) +{ + memset(in_vk_keys_down, 0, sizeof(in_vk_keys_down)); + in_register(IN_VK_PREFIX "vk", IN_DRVID_VK, -1, (void *)1, IN_VK_NKEYS, 0); +} + +static int in_vk_get_bind_count(void) +{ + return IN_VK_NKEYS; +} + +/* ORs result with pressed buttons */ +int in_vk_update(void *drv_data, const int *binds, int *result) +{ + int i, t, k; + + for (i = 0; i < array_size(in_vk_keys_down); i++) { + k = in_vk_keys_down[i]; + if (!k) + continue; + + for (t = 0; t < IN_BINDTYPE_COUNT; t++) + result[t] |= binds[IN_BIND_OFFS(k, t)]; + } + + result[IN_BINDTYPE_PLAYER12] |= in_vk_add_pl12; + + return 0; +} + +void in_vk_keydown(int kc) +{ + int i; + + // search + for (i = 0; i < array_size(in_vk_keys_down); i++) + if (in_vk_keys_down[i] == kc) + return; + + // do + for (i = 0; i < array_size(in_vk_keys_down); i++) { + if (in_vk_keys_down[i] == 0) { + in_vk_keys_down[i] = kc; + return; + } + } +} + +void in_vk_keyup(int kc) +{ + int i; + for (i = 0; i < array_size(in_vk_keys_down); i++) + if (in_vk_keys_down[i] == kc) + in_vk_keys_down[i] = 0; +} + +static int in_vk_update_keycode(void *data, int *is_down) +{ + return 0; +} + +static const struct { + short key; + short pbtn; +} key_pbtn_map[] = +{ + { VK_UP, PBTN_UP }, + { VK_DOWN, PBTN_DOWN }, + { VK_LEFT, PBTN_LEFT }, + { VK_RIGHT, PBTN_RIGHT }, + { VK_RETURN, PBTN_MOK }, +/* + { BTN_X, PBTN_MBACK }, + { BTN_A, PBTN_MA2 }, + { BTN_Y, PBTN_MA3 }, + { BTN_L, PBTN_L }, + { BTN_R, PBTN_R }, + { BTN_SELECT, PBTN_MENU }, +*/ +}; + +#define KEY_PBTN_MAP_SIZE (sizeof(key_pbtn_map) / sizeof(key_pbtn_map[0])) + +static int in_vk_menu_translate(int keycode) +{ + int i; + if (keycode < 0) + { + /* menu -> kc */ + keycode = -keycode; + for (i = 0; i < KEY_PBTN_MAP_SIZE; i++) + if (key_pbtn_map[i].pbtn == keycode) + return key_pbtn_map[i].key; + } + else + { + for (i = 0; i < KEY_PBTN_MAP_SIZE; i++) + if (key_pbtn_map[i].key == keycode) + return key_pbtn_map[i].pbtn; + } + + return 0; +} + +static int in_vk_get_key_code(const char *key_name) +{ + int i; + + if (key_name[1] == 0 && 'A' <= key_name[0] && key_name[0] <= 'Z') + return key_name[0]; + + for (i = 0; i < IN_VK_NKEYS; i++) { + const char *k = in_vk_keys[i]; + if (k != NULL && strcasecmp(k, key_name) == 0) + return i; + } + + return -1; +} + +static const char *in_vk_get_key_name(int keycode) +{ + const char *name = NULL; + static char buff[4]; + + if ('A' <= keycode && keycode < 'Z') { + buff[0] = keycode; + buff[1] = 0; + return buff; + } + + if (0 <= keycode && keycode < IN_VK_NKEYS) + name = in_vk_keys[keycode]; + if (name == NULL) + name = "Unkn"; + + return name; +} + +static const struct { + short code; + char btype; + char bit; +} in_vk_def_binds[] = +{ + /* MXYZ SACB RLDU */ + { VK_UP, IN_BINDTYPE_PLAYER12, 0 }, + { VK_DOWN, IN_BINDTYPE_PLAYER12, 1 }, + { VK_LEFT, IN_BINDTYPE_PLAYER12, 2 }, + { VK_RIGHT, IN_BINDTYPE_PLAYER12, 3 }, + { 'S', IN_BINDTYPE_PLAYER12, 4 }, /* B */ + { 'D', IN_BINDTYPE_PLAYER12, 5 }, /* C */ + { 'A', IN_BINDTYPE_PLAYER12, 6 }, /* A */ + { VK_RETURN, IN_BINDTYPE_PLAYER12, 7 }, +/* + { BTN_SELECT, IN_BINDTYPE_EMU, PEVB_MENU }, +// { BTN_Y, IN_BINDTYPE_EMU, PEVB_SWITCH_RND }, + { BTN_L, IN_BINDTYPE_EMU, PEVB_STATE_SAVE }, + { BTN_R, IN_BINDTYPE_EMU, PEVB_STATE_LOAD }, + { BTN_VOL_UP, IN_BINDTYPE_EMU, PEVB_VOL_UP }, + { BTN_VOL_DOWN, IN_BINDTYPE_EMU, PEVB_VOL_DOWN }, +*/ +}; + +#define DEF_BIND_COUNT (sizeof(in_vk_def_binds) / sizeof(in_vk_def_binds[0])) + +static void in_vk_get_def_binds(int *binds) +{ + int i; + + for (i = 0; i < DEF_BIND_COUNT; i++) + binds[IN_BIND_OFFS(in_vk_def_binds[i].code, in_vk_def_binds[i].btype)] = + 1 << in_vk_def_binds[i].bit; +} + +/* remove binds of missing keys, count remaining ones */ +static int in_vk_clean_binds(void *drv_data, int *binds, int *def_binds) +{ + int i, count = 0; + + for (i = 0; i < IN_VK_NKEYS; i++) { + int t, offs; + for (t = 0; t < IN_BINDTYPE_COUNT; t++) { + offs = IN_BIND_OFFS(i, t); + if (strcmp(in_vk_get_key_name(i), "Unkn") == 0) + binds[offs] = def_binds[offs] = 0; + if (binds[offs]) + count++; + } + } + + return count; +} + +void in_vk_init(void *vdrv) +{ + in_drv_t *drv = vdrv; + + drv->prefix = in_vk_prefix; + drv->probe = in_vk_probe; + drv->get_bind_count = in_vk_get_bind_count; + drv->get_def_binds = in_vk_get_def_binds; + drv->clean_binds = in_vk_clean_binds; + drv->menu_translate = in_vk_menu_translate; + drv->get_key_code = in_vk_get_key_code; + drv->get_key_name = in_vk_get_key_name; + drv->update_keycode = in_vk_update_keycode; +} + diff --git a/win32/in_vk.h b/win32/in_vk.h new file mode 100644 index 0000000..8bc7e98 --- /dev/null +++ b/win32/in_vk.h @@ -0,0 +1,16 @@ +#ifdef IN_VK + +void in_vk_init(void *vdrv); +int in_vk_update(void *drv_data, const int *binds, int *result); + +void in_vk_keydown(int kc); +void in_vk_keyup(int kc); + +extern int in_vk_add_pl12; + +#else + +#define in_vk_init(x) +#define in_vk_update(a,b,c) 0 + +#endif diff --git a/win32/main.c b/win32/main.c new file mode 100644 index 0000000..17191e2 --- /dev/null +++ b/win32/main.c @@ -0,0 +1,635 @@ +#include +#include +#include + +#include "../../pico/pico.h" +#include "../common/readpng.h" +#include "../common/config.h" +#include "../common/lprintf.h" +#include "../common/emu.h" +#include "../common/menu.h" +#include "../common/input.h" +#include "../common/plat.h" +#include "version.h" +#include "direct.h" +#include "in_vk.h" + +char *romname=NULL; +HWND FrameWnd=NULL; +RECT FrameRectMy; +RECT EmuScreenRect = { 0, 0, 320, 224 }; +int lock_to_1_1 = 1; +static HWND PicoSwWnd=NULL, PicoPadWnd=NULL; + +static HMENU mmain = 0, mdisplay = 0, mpicohw = 0; +static HBITMAP ppad_bmp = 0; +static HBITMAP ppage_bmps[7] = { 0, }; +static char rom_name[0x20*3+1]; +static int main_wnd_as_pad = 0; + +static HANDLE loop_enter_event, loop_end_event; + +void error(char *text) +{ + MessageBox(FrameWnd, text, "Error", 0); +} + +static void UpdateRect(void) +{ + WINDOWINFO wi; + memset(&wi, 0, sizeof(wi)); + wi.cbSize = sizeof(wi); + GetWindowInfo(FrameWnd, &wi); + FrameRectMy = wi.rcClient; +} + +static int extract_rom_name(char *dest, const unsigned char *src, int len) +{ + char *p = dest, s_old = 0x20; + int i; + + for (i = len - 1; i >= 0; i--) + { + if (src[i^1] != ' ') break; + } + len = i + 1; + + for (i = 0; i < len; i++) + { + unsigned char s = src[i^1]; + if (s == 0x20 && s_old == 0x20) continue; + else if (s >= 0x20 && s < 0x7f && s != '%') + { + *p++ = s; + } + else + { + sprintf(p, "%%%02x", s); + p += 3; + } + s_old = s; + } + *p = 0; + + return p - dest; +} + +static void check_name_alias(const char *afname) +{ + char buff[256], *var, *val; + FILE *f; + int ret; + + f = fopen(afname, "r"); + if (f == NULL) return; + + while (1) + { + ret = config_get_var_val(f, buff, sizeof(buff), &var, &val); + if (ret == 0) break; + if (ret == -1) continue; + + if (strcmp(rom_name, var) == 0) { + lprintf("rom aliased: \"%s\" -> \"%s\"\n", rom_name, val); + strncpy(rom_name, val, sizeof(rom_name)); + break; + } + } + fclose(f); +} + +static HBITMAP png2hb(const char *fname, int is_480) +{ + BITMAPINFOHEADER bih; + HBITMAP bmp; + void *bmem; + int ret; + + bmem = calloc(1, is_480 ? 480*240*3 : 320*240*3); + if (bmem == NULL) return NULL; + ret = readpng(bmem, fname, is_480 ? READPNG_480_24 : READPNG_320_24); + if (ret != 0) { + free(bmem); + return NULL; + } + + memset(&bih, 0, sizeof(bih)); + bih.biSize = sizeof(bih); + bih.biWidth = is_480 ? 480 : 320; + bih.biHeight = -240; + bih.biPlanes = 1; + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + bmp = CreateDIBitmap(GetDC(FrameWnd), &bih, CBM_INIT, bmem, (BITMAPINFO *)&bih, 0); + if (bmp == NULL) + lprintf("CreateDIBitmap failed with %i", GetLastError()); + + free(bmem); + return bmp; +} + +static void PrepareForROM(void) +{ + unsigned char *rom_data = NULL; + int i, ret, show = PicoAHW & PAHW_PICO; + + PicoGetInternal(PI_ROM, (pint_ret_t *) &rom_data); + EnableMenuItem(mmain, 2, MF_BYPOSITION|(show ? MF_ENABLED : MF_GRAYED)); + ShowWindow(PicoPadWnd, show ? SW_SHOWNA : SW_HIDE); + ShowWindow(PicoSwWnd, show ? SW_SHOWNA : SW_HIDE); + CheckMenuItem(mpicohw, 1210, show ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(mpicohw, 1211, show ? MF_CHECKED : MF_UNCHECKED); + PostMessage(FrameWnd, WM_COMMAND, 1220 + PicoPicohw.page, 0); + DrawMenuBar(FrameWnd); + InvalidateRect(PicoSwWnd, NULL, 1); + + PicoPicohw.pen_pos[0] = + PicoPicohw.pen_pos[1] = 0x8000; + in_vk_add_pl12 = 0; + + ret = extract_rom_name(rom_name, rom_data + 0x150, 0x20); + if (ret == 0) + extract_rom_name(rom_name, rom_data + 0x130, 0x20); + + if (show) + { + char path[MAX_PATH], *p; + GetModuleFileName(NULL, path, sizeof(path) - 32); + p = strrchr(path, '\\'); + if (p == NULL) p = path; + else p++; + if (ppad_bmp == NULL) { + strcpy(p, "pico\\pad.png"); + ppad_bmp = png2hb(path, 0); + } + + strcpy(p, "pico\\alias.txt"); + check_name_alias(path); + + for (i = 0; i < 7; i++) { + if (ppage_bmps[i] != NULL) DeleteObject(ppage_bmps[i]); + sprintf(p, "pico\\%s_%i.png", rom_name, i); + ppage_bmps[i] = png2hb(path, 1); + } + // games usually don't have page 6, so just duplicate page 5. + if (ppage_bmps[6] == NULL && ppage_bmps[5] != NULL) { + sprintf(p, "pico\\%s_5.png", rom_name); + ppage_bmps[6] = png2hb(path, 1); + } + } +} + +static void LoadROM(const char *cmdpath) +{ + char rompath[MAX_PATH]; + int ret; + + if (cmdpath != NULL && strlen(cmdpath)) { + strcpy(rompath, cmdpath + (cmdpath[0] == '\"' ? 1 : 0)); + if (rompath[strlen(rompath)-1] == '\"') + rompath[strlen(rompath)-1] = 0; + } + else { + OPENFILENAME of; ZeroMemory(&of, sizeof(of)); + rompath[sizeof(rompath) - 1] = 0; + strncpy(rompath, rom_fname_loaded, sizeof(rompath) - 1); + of.lStructSize = sizeof(of); + of.lpstrFilter = "ROMs, CD images\0*.smd;*.bin;*.gen;*.zip;*.32x;*.sms;*.iso;*.cso;*.cue\0" + "whatever\0*.*\0"; + of.lpstrFile = rompath; + of.nMaxFile = MAX_PATH; + of.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; + of.hwndOwner = FrameWnd; + if (!GetOpenFileName(&of)) + return; + } + + if (engineState == PGS_Running) { + engineState = PGS_Paused; + WaitForSingleObject(loop_end_event, 5000); + } + + ret = emu_reload_rom(rompath); + if (ret == 0) { + extern char menu_error_msg[]; // HACK.. + error(menu_error_msg); + return; + } + + PrepareForROM(); + engineState = PGS_Running; + SetEvent(loop_enter_event); +} + +static const int rect_widths[4] = { 320, 256, 640, 512 }; +static const int rect_heights[4] = { 224, 224, 448, 448 }; + +// Window proc for the frame window: +static LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam) +{ + POINT pt; + RECT rc; + int i; + switch (msg) + { + case WM_CLOSE: + PostQuitMessage(0); + return 0; + case WM_DESTROY: + FrameWnd = NULL; // Blank the handle + break; + case WM_SIZE: + case WM_MOVE: + case WM_SIZING: + UpdateRect(); + if (lock_to_1_1 && FrameRectMy.right - FrameRectMy.left != 0 && + (FrameRectMy.right - FrameRectMy.left != EmuScreenRect.right - EmuScreenRect.left || + FrameRectMy.bottom - FrameRectMy.top != EmuScreenRect.bottom - EmuScreenRect.top)) { + lock_to_1_1 = 0; + CheckMenuItem(mdisplay, 1104, MF_UNCHECKED); + } + break; + case WM_COMMAND: + switch (LOWORD(wparam)) + { + case 1000: + LoadROM(NULL); + break; + case 1001: + emu_reset_game(); + return 0; + case 1002: + PostQuitMessage(0); + return 0; + case 1100: + case 1101: + case 1102: + case 1103: +// LoopWait=1; // another sync hack +// for (i = 0; !LoopWaiting && i < 10; i++) Sleep(10); + FrameRectMy.right = FrameRectMy.left + rect_widths[wparam&3]; + FrameRectMy.bottom = FrameRectMy.top + rect_heights[wparam&3]; + AdjustWindowRect(&FrameRectMy, WS_OVERLAPPEDWINDOW, 1); + MoveWindow(hwnd, FrameRectMy.left, FrameRectMy.top, + FrameRectMy.right-FrameRectMy.left, FrameRectMy.bottom-FrameRectMy.top, 1); + UpdateRect(); + lock_to_1_1 = 0; + CheckMenuItem(mdisplay, 1104, MF_UNCHECKED); +// if (rom_loaded) LoopWait=0; + return 0; + case 1104: + lock_to_1_1 = !lock_to_1_1; + CheckMenuItem(mdisplay, 1104, lock_to_1_1 ? MF_CHECKED : MF_UNCHECKED); + /* FALLTHROUGH */ + case 2000: // EmuScreenRect/FrameRectMy sync request + if (!lock_to_1_1) + return 0; + FrameRectMy.right = FrameRectMy.left + (EmuScreenRect.right - EmuScreenRect.left); + FrameRectMy.bottom = FrameRectMy.top + (EmuScreenRect.bottom - EmuScreenRect.top); + AdjustWindowRect(&FrameRectMy, WS_OVERLAPPEDWINDOW, 1); + MoveWindow(hwnd, FrameRectMy.left, FrameRectMy.top, + FrameRectMy.right-FrameRectMy.left, FrameRectMy.bottom-FrameRectMy.top, 1); + UpdateRect(); + return 0; + case 1210: + case 1211: + i = IsWindowVisible((LOWORD(wparam)&1) ? PicoPadWnd : PicoSwWnd); + i = !i; + ShowWindow((LOWORD(wparam)&1) ? PicoPadWnd : PicoSwWnd, i ? SW_SHOWNA : SW_HIDE); + CheckMenuItem(mpicohw, LOWORD(wparam), i ? MF_CHECKED : MF_UNCHECKED); + return 0; + case 1212: + main_wnd_as_pad = !main_wnd_as_pad; + CheckMenuItem(mpicohw, 1212, main_wnd_as_pad ? MF_CHECKED : MF_UNCHECKED); + return 0; + case 1220: + case 1221: + case 1222: + case 1223: + case 1224: + case 1225: + case 1226: + PicoPicohw.page = LOWORD(wparam) % 10; + for (i = 0; i < 7; i++) + CheckMenuItem(mpicohw, 1220 + i, MF_UNCHECKED); + CheckMenuItem(mpicohw, 1220 + PicoPicohw.page, MF_CHECKED); + InvalidateRect(PicoSwWnd, NULL, 1); + return 0; + case 1300: + MessageBox(FrameWnd, plat_get_credits(), "About", 0); + return 0; + } + break; + case WM_TIMER: + GetCursorPos(&pt); + GetWindowRect(PicoSwWnd, &rc); + if (PtInRect(&rc, pt)) break; + GetWindowRect(PicoPadWnd, &rc); + if (PtInRect(&rc, pt)) break; + PicoPicohw.pen_pos[0] |= 0x8000; + PicoPicohw.pen_pos[1] |= 0x8000; + in_vk_add_pl12 = 0; + break; + case WM_LBUTTONDOWN: in_vk_add_pl12 |= 0x20; return 0; + case WM_LBUTTONUP: in_vk_add_pl12 &= ~0x20; return 0; + case WM_MOUSEMOVE: + if (!main_wnd_as_pad) break; + PicoPicohw.pen_pos[0] = 0x03c + (320 * LOWORD(lparam) / (FrameRectMy.right - FrameRectMy.left)); + PicoPicohw.pen_pos[1] = 0x1fc + (232 * HIWORD(lparam) / (FrameRectMy.bottom - FrameRectMy.top)); + SetTimer(FrameWnd, 100, 1000, NULL); + break; + case WM_KEYDOWN: + if (wparam == VK_TAB) { + emu_reset_game(); + break; + } + if (wparam == VK_ESCAPE) { + LoadROM(NULL); + break; + } + in_vk_keydown(wparam); + break; + case WM_KEYUP: + in_vk_keyup(wparam); + break; + } + + return DefWindowProc(hwnd,msg,wparam,lparam); +} + +static LRESULT CALLBACK PicoSwWndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam) +{ + PAINTSTRUCT ps; + HDC hdc, hdc2; + + switch (msg) + { + case WM_DESTROY: PicoSwWnd=NULL; break; + case WM_LBUTTONDOWN: in_vk_add_pl12 |= 0x20; return 0; + case WM_LBUTTONUP: in_vk_add_pl12 &= ~0x20; return 0; + case WM_MOUSEMOVE: + if (HIWORD(lparam) < 0x20) break; + PicoPicohw.pen_pos[0] = 0x03c + LOWORD(lparam) * 2/3; + PicoPicohw.pen_pos[1] = 0x2f8 + HIWORD(lparam) - 0x20; + SetTimer(FrameWnd, 100, 1000, NULL); + break; + case WM_KEYDOWN: in_vk_keydown(wparam); break; + case WM_KEYUP: in_vk_keyup(wparam); break; + case WM_PAINT: + hdc = BeginPaint(hwnd, &ps); + if (ppage_bmps[PicoPicohw.page] == NULL) + { + SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); + SetTextColor(hdc, RGB(255, 255, 255)); + SetBkColor(hdc, RGB(0, 0, 0)); + TextOut(hdc, 2, 2, "missing PNGs for", 16); + TextOut(hdc, 2, 18, rom_name, strlen(rom_name)); + } + else + { + hdc2 = CreateCompatibleDC(GetDC(FrameWnd)); + SelectObject(hdc2, ppage_bmps[PicoPicohw.page]); + BitBlt(hdc, 0, 0, 480, 240, hdc2, 0, 0, SRCCOPY); + DeleteDC(hdc2); + } + EndPaint(hwnd, &ps); + return 0; + case WM_CLOSE: + ShowWindow(hwnd, SW_HIDE); + CheckMenuItem(mpicohw, 1210, MF_UNCHECKED); + return 0; + } + + return DefWindowProc(hwnd,msg,wparam,lparam); +} + +static LRESULT CALLBACK PicoPadWndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam) +{ + PAINTSTRUCT ps; + HDC hdc, hdc2; + + switch (msg) + { + case WM_DESTROY: PicoPadWnd=NULL; break; + case WM_LBUTTONDOWN: in_vk_add_pl12 |= 0x20; return 0; + case WM_LBUTTONUP: in_vk_add_pl12 &= ~0x20; return 0; + case WM_MOUSEMOVE: + PicoPicohw.pen_pos[0] = 0x03c + LOWORD(lparam); + PicoPicohw.pen_pos[1] = 0x1fc + HIWORD(lparam); + SetTimer(FrameWnd, 100, 1000, NULL); + break; + case WM_KEYDOWN: in_vk_keydown(wparam); break; + case WM_KEYUP: in_vk_keyup(wparam); break; + case WM_PAINT: + if (ppad_bmp == NULL) break; + hdc = BeginPaint(hwnd, &ps); + hdc2 = CreateCompatibleDC(GetDC(FrameWnd)); + SelectObject(hdc2, ppad_bmp); + BitBlt(hdc, 0, 0, 320, 240, hdc2, 0, 0, SRCCOPY); + EndPaint(hwnd, &ps); + DeleteDC(hdc2); + return 0; + case WM_CLOSE: + ShowWindow(hwnd, SW_HIDE); + CheckMenuItem(mpicohw, 1211, MF_UNCHECKED); + return 0; + } + + return DefWindowProc(hwnd,msg,wparam,lparam); +} + + +static int FrameInit() +{ + WNDCLASS wc; + RECT rect={0,0,0,0}; + HMENU mfile; + int style=0; + int left=0,top=0,width=0,height=0; + + memset(&wc,0,sizeof(wc)); + + // Register the window class: + wc.lpfnWndProc=WndProc; + wc.hInstance=GetModuleHandle(NULL); + wc.hCursor=LoadCursor(NULL,IDC_ARROW); + wc.hbrBackground=CreateSolidBrush(0); + wc.lpszClassName="PicoMainFrame"; + RegisterClass(&wc); + + wc.lpszClassName="PicoSwWnd"; + wc.lpfnWndProc=PicoSwWndProc; + RegisterClass(&wc); + + wc.lpszClassName="PicoPadWnd"; + wc.lpfnWndProc=PicoPadWndProc; + RegisterClass(&wc); + + rect.right =320; + rect.bottom=224; + + // Adjust size of windows based on borders: + style=WS_OVERLAPPEDWINDOW; + AdjustWindowRect(&rect,style,1); + width =rect.right-rect.left; + height=rect.bottom-rect.top; + + // Place window in the centre of the screen: + SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0); + left=rect.left+rect.right; + top=rect.top+rect.bottom; + + left-=width; left>>=1; + top-=height; top>>=1; + + // Create menu: + mfile = CreateMenu(); + InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1000, "&Load ROM"); + InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1001, "&Reset"); + InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1002, "E&xit"); + mdisplay = CreateMenu(); + InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1100, "320x224"); + InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1101, "256x224"); + InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1102, "640x448"); + InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1103, "512x448"); + InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1104, "Lock to 1:1"); + mpicohw = CreateMenu(); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1210, "Show &Storyware"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1211, "Show &Drawing pad"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1212, "&Main window as pad"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1220, "Title page (&0)"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1221, "Page &1"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1222, "Page &2"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1223, "Page &3"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1224, "Page &4"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1225, "Page &5"); + InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1226, "Page &6"); + mmain = CreateMenu(); + InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mfile, "&File"); + InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mdisplay, "&Display"); + InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mpicohw, "&Pico"); + EnableMenuItem(mmain, 2, MF_BYPOSITION|MF_GRAYED); +// InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, 1200, "&Config"); + InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING, 1300, "&About"); + + // Create the window: + FrameWnd=CreateWindow("PicoMainFrame","PicoDrive " VERSION,style|WS_VISIBLE, + left,top,width,height,NULL,mmain,NULL,NULL); + + CheckMenuItem(mdisplay, 1104, lock_to_1_1 ? MF_CHECKED : MF_UNCHECKED); + ShowWindow(FrameWnd, SW_NORMAL); + UpdateWindow(FrameWnd); + UpdateRect(); + + // create Pico windows + style = WS_OVERLAPPED|WS_CAPTION|WS_BORDER|WS_SYSMENU; + rect.left=rect.top=0; + rect.right =320; + rect.bottom=224; + + AdjustWindowRect(&rect,style,1); + width =rect.right-rect.left; + height=rect.bottom-rect.top; + + left += 326; + PicoSwWnd=CreateWindow("PicoSwWnd","Storyware",style, + left,top,width+160,height,FrameWnd,NULL,NULL,NULL); + + top += 266; + PicoPadWnd=CreateWindow("PicoPadWnd","Drawing Pad",style, + left,top,width,height,FrameWnd,NULL,NULL,NULL); + + return 0; +} + +// -------------------- + +static DWORD WINAPI work_thread(void *x) +{ + while (engineState != PGS_Quit) { + WaitForSingleObject(loop_enter_event, INFINITE); + if (engineState != PGS_Running) + continue; + + printf("loop..\n"); + emu_loop(); + SetEvent(loop_end_event); + } + + return 0; +} + +// XXX: use main.c +void xxinit(void) +{ + /* in_init() must go before config, config accesses in_ fwk */ + in_init(); + pemu_prep_defconfig(); + emu_read_config(0, 0); + config_readlrom(PicoConfigFile); + + plat_init(); + in_probe(); + + emu_init(); + menu_init(); +} + + +int WINAPI WinMain(HINSTANCE p1, HINSTANCE p2, LPSTR cmdline, int p4) +{ + MSG msg; + DWORD tid = 0; + HANDLE thread; + int ret; + + xxinit(); + FrameInit(); + ret = DirectInit(); + if (ret) + goto end0; + + loop_enter_event = CreateEvent(NULL, 0, 0, NULL); + if (loop_enter_event == NULL) + goto end0; + + loop_end_event = CreateEvent(NULL, 0, 0, NULL); + if (loop_end_event == NULL) + goto end0; + + thread = CreateThread(NULL, 0, work_thread, NULL, 0, &tid); + if (thread == NULL) + goto end0; + + LoadROM(cmdline); + + // Main window loop: + for (;;) + { + GetMessage(&msg,NULL,0,0); + if (msg.message==WM_QUIT) break; + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Signal thread to quit and wait for it to exit: + if (engineState == PGS_Running) { + engineState = PGS_Quit; + WaitForSingleObject(loop_end_event, 5000); + } + CloseHandle(thread); thread=NULL; + + emu_write_config(0); + emu_finish(); + //plat_finish(); + +end0: + DirectExit(); + DestroyWindow(FrameWnd); + +// _CrtDumpMemoryLeaks(); + return 0; +} + diff --git a/win32/main.h b/win32/main.h new file mode 100644 index 0000000..7186b52 --- /dev/null +++ b/win32/main.h @@ -0,0 +1,11 @@ +#ifdef __cplusplus +extern "C" { +#endif + +extern HWND FrameWnd; +extern RECT FrameRectMy; +extern RECT EmuScreenRect; + +#ifdef __cplusplus +} +#endif diff --git a/win32/plat.c b/win32/plat.c new file mode 100644 index 0000000..0c1dea6 --- /dev/null +++ b/win32/plat.c @@ -0,0 +1,259 @@ +#include +#include + +#include "../common/lprintf.h" +#include "../common/plat.h" +#include "../common/emu.h" +#include "../../pico/pico.h" +#include "version.h" +#include "direct.h" +#include "dsnd.h" +#include "main.h" + +static unsigned short screen_buff[320 * 240]; +static unsigned char PicoDraw2FB_[(8+320) * (8+240+8)]; +unsigned char *PicoDraw2FB = PicoDraw2FB_; + +char cpu_clk_name[] = "unused"; + +void plat_init(void) +{ + g_screen_ptr = (void *)screen_buff; +} + +int plat_is_dir(const char *path) +{ + return (GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; +} + +unsigned int plat_get_ticks_ms(void) +{ + return GetTickCount(); +} + +unsigned int plat_get_ticks_us(void) +{ + // XXX: maybe performance counters? + return GetTickCount() * 1000; +} + +void plat_wait_till_us(unsigned int us) +{ + int msdiff = (int)(us - plat_get_ticks_us()) / 1000; + if (msdiff > 6) +;// Sleep(msdiff - 6); + while (plat_get_ticks_us() < us) + ; +} + +void plat_sleep_ms(int ms) +{ + Sleep(ms); +} + +int plat_wait_event(int *fds_hnds, int count, int timeout_ms) +{ + return -1; +} + +void pemu_prep_defconfig(void) +{ + memset(&defaultConfig, 0, sizeof(defaultConfig)); + defaultConfig.EmuOpt = 0x9d | EOPT_RAM_TIMINGS|EOPT_CONFIRM_SAVE|EOPT_EN_CD_LEDS; + defaultConfig.s_PicoOpt = POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 | + POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_ACC_SPRITES | + POPT_EN_32X|POPT_EN_PWM; + defaultConfig.s_PicoOpt|= POPT_6BTN_PAD; // for xmen proto + defaultConfig.s_PsndRate = 44100; + defaultConfig.s_PicoRegion = 0; // auto + defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP + defaultConfig.s_PicoCDBuffers = 0; + defaultConfig.Frameskip = 0; +} + +static int EmuScanBegin16(unsigned int num) +{ + DrawLineDest = (unsigned short *) g_screen_ptr + g_screen_width * num; + + return 0; +} + +void pemu_loop_prep(void) +{ + PicoDrawSetColorFormat(1); + PicoScanBegin = EmuScanBegin16; + pemu_sound_start(); +} + +void pemu_loop_end(void) +{ + pemu_sound_stop(); +} + +void pemu_forced_frame(int opts) +{ +} + +void pemu_update_display(const char *fps, const char *notice_msg) +{ + DirectScreen(g_screen_ptr); + DirectPresent(); +} + +void plat_video_wait_vsync(void) +{ +} + +void plat_video_toggle_renderer(int is_next, int force_16bpp, int is_menu) +{ + // this will auto-select SMS/32X renderers + PicoDrawSetColorFormat(1); +} + +void emu_video_mode_change(int start_line, int line_count, int is_32cols) +{ + EmuScreenRect.left = is_32cols ? 32 : 0; + EmuScreenRect.right = is_32cols ? 256+32 : 320; + EmuScreenRect.top = start_line; + EmuScreenRect.bottom = start_line + line_count; + + PostMessage(FrameWnd, WM_COMMAND, 0x20000 | 2000, 0); +} + +static int sndbuff[2*44100/50/2 + 4]; + +static void update_sound(int len) +{ + /* avoid writing audio when lagging behind to prevent audio lag */ + if (PicoSkipFrame != 2) + DSoundUpdate(sndbuff, (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) ? 0 : 1); +} + +void pemu_sound_start(void) +{ + int ret; + + PsndOut = NULL; + currentConfig.EmuOpt &= ~EOPT_EXT_FRMLIMIT; + + // prepare sound stuff + if (currentConfig.EmuOpt & EOPT_EN_SOUND) + { + PsndRerate(0); + + ret = DSoundInit(FrameWnd, PsndRate, (PicoOpt & POPT_EN_STEREO) ? 1 : 0, PsndLen); + if (ret != 0) { + lprintf("dsound init failed\n"); + return; + } + + PsndOut = (void *)sndbuff; + PicoWriteSound = update_sound; + currentConfig.EmuOpt |= EOPT_EXT_FRMLIMIT; + } +} + +void pemu_sound_stop(void) +{ + DSoundExit(); +} + +void pemu_sound_wait(void) +{ +} + +int plat_get_root_dir(char *dst, int len) +{ + int ml; + + ml = GetModuleFileName(NULL, dst, len); + while (ml > 0 && dst[ml] != '\\') + ml--; + if (ml != 0) + ml++; + + dst[ml] = 0; + return ml; +} + +void plat_status_msg_busy_first(const char *msg) +{ +} + +void plat_status_msg_busy_next(const char *msg) +{ +} + +void plat_status_msg_clear(void) +{ +} + +void plat_video_menu_enter(int is_rom_loaded) +{ +} + +void plat_video_menu_begin(void) +{ +} + +void plat_video_menu_end(void) +{ +} + +void plat_validate_config(void) +{ +} + +void plat_update_volume(int has_changed, int is_up) +{ +} + +const char *plat_get_credits(void) +{ + return "PicoDrive v" VERSION " minibeta (c) notaz, 2006-2009\n\n" + "Credits:\n" + "fDave: base code of PicoDrive\n" + "Chui: Fame/C\n" + "NJ: CZ80\n" + "MAME devs: YM2612, SN76496 and SH2 cores\n" + "Stéphane Dallongeville: base of Fame/C (C68K), CZ80\n\n" + "Special thanks (ideas, valuable information and stuff):\n" + "Charles MacDonald, Eke, Exophase, Haze, Lordus, Nemesis,\n" + "Pierpaolo Prazzoli, Rokas, Steve Snake, Tasco Deluxe.\n"; +} + +void plat_debug_cat(char *str) +{ +} + +// required by pico +int mp3_get_bitrate(FILE *f, int size) +{ + return 128; +} + +void mp3_start_play(FILE *f, int pos) +{ +} + +void mp3_update(int *buffer, int length, int stereo) +{ +} + +// other +void lprintf(const char *fmt, ...) +{ + char buf[512]; + va_list val; + + va_start(val, fmt); + vsnprintf(buf, sizeof(buf), fmt, val); + va_end(val); + OutputDebugString(buf); + printf("%s", buf); +} + +// fake +int alphasort() { return 0; } +int scandir() { return 0; } + diff --git a/win32/port_config.h b/win32/port_config.h new file mode 100644 index 0000000..04b4c93 --- /dev/null +++ b/win32/port_config.h @@ -0,0 +1,40 @@ +// port specific settings + +#ifndef PORT_CONFIG_H +#define PORT_CONFIG_H + +#define NO_SYNC + +#define CASE_SENSITIVE_FS 0 // CS filesystem +#define DONT_OPEN_MANY_FILES 0 +#define REDUCE_IO_CALLS 0 + +#define SCREEN_SIZE_FIXED 0 +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 240 + +// draw.c +#define OVERRIDE_HIGHCOL 0 + +// draw2.c +#define START_ROW 0 // which row of tiles to start rendering at? +#define END_ROW 28 // ..end + +// pico.c +#define CAN_HANDLE_240_LINES 1 + +#define SIMPLE_WRITE_SOUND 1 +#define mix_32_to_16l_stereo_lvl mix_32_to_16l_stereo + +#define EL_LOGMASK (EL_STATUS) + +//#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__) +#define dprintf(x...) + +// platform +#define PATH_SEP "\\" +#define PATH_SEP_C '\\' +#define MENU_X2 0 + +#endif //PORT_CONFIG_H + diff --git a/win32/readme.txt b/win32/readme.txt new file mode 100644 index 0000000..ba9ebff --- /dev/null +++ b/win32/readme.txt @@ -0,0 +1,69 @@ + +About +----- + +This is a quick windows port of PicoDrive, a Megadrive / Genesis emulator for +handheld devices. It was originally coded having ARM CPU based devices in mind +(most work was done on GP2X version), but there is also a PSP port. + +The reason I'm sometimes doing windows versions is to show certain emulation +possibilities, first release was to demonstrate SVP emulation (Virtua Racing), +later Pico toy and X-Men 32X prototype. It is not to compete with other +emulators like Kega Fusion and the likes. + +For more info, visit http://notaz.gp2x.de/svp.php + + +Releases +-------- + +1.70 - preliminary 32X emulation, runs X-Men proto. +1.45a - Few bugfixes and additions. +1.45 - Added preliminary Sega Pico emulation. +1.40b - Perspective fix thanks to Pierpaolo Prazzoli's info. +1.40a - Tasco Deluxe's dithering fix. +1.40 - first release. + + +Controls +-------- + +These are currently hardcoded, keyboard only: + +PC Gen/MD Sega Pico +-------+-----------+--------- +Enter: Start +A: A +S: B red button +D: C pen push +TAB: (reset) +Esc: (load ROM) +Arrows: D-pad + +It is possible to change some things in config.cfg (it is created on exit), +but possibilities are limited. + + +Credits +------- + +Vast majority of code written by notaz (notasasatgmailcom). + +A lot of work on making SVP emulation happen was done by Tasco Deluxe, my +stuff is a continuation of his. Pierpaolo Prazzoli's information and his +SSP1610 disassembler in MAME code helped a lot too. + +The original PicoDrive was written by fDave from finalburn.com + +This PicoDrive version uses bits and pieces of from other projects: + +68k: FAME/C core, by Chui and Stéphane Dallongeville (as C68K). +z80: CZ80 by Stéphane Dallongeville and modified by NJ. +YM2612, SN76496 and SH2 cores: MAME devs. + +Special thanks (ideas, valuable information and stuff): +Charles MacDonald, Eke, Exophase, Haze, Lordus, Nemesis, +Pierpaolo Prazzoli, Rokas, Steve Snake, Tasco Deluxe. + +Greets to all the sceners and emu authors out there! + diff --git a/win32/version.h b/win32/version.h new file mode 100644 index 0000000..69cf800 --- /dev/null +++ b/win32/version.h @@ -0,0 +1,2 @@ +#define VERSION "1.70" +