#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "apu/apu.h" #include "blit.h" #include "conffile.h" #include "controls.h" #include "display.h" #include "gfx.h" #include "hifb.hpp" #include "hq2x.h" #include "memmap.h" #include "snes9x.h" #define MAP_KEY(x, y) \ { \ s9xcommand_t cmd; \ \ cmd = S9xGetCommandT(y); \ S9xMapInput(x, &cmd); \ } HI_U32 g_pSnesBackBufferPhys; void *g_pSnesBackBufferVirt; HI_U32 g_pScaleBufferPhys; void *g_pScaleBufferVirt; uint8 js_mod[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int js_fd[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; const char *js_device[8] = {"/dev/js0", "/dev/js1", "/dev/js2", "/dev/js3", "/dev/js4", "/dev/js5", "/dev/js6", "/dev/js7"}; bool8 js_unplugged[8] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; void S9xCloseSnapshotFile(STREAM file) {} void S9xExit(void) {} void S9xAutoSaveSRAM(void) {} void S9xToggleSoundChannel(int c) {} void S9xSyncSpeed(void) {} void S9xMessage(int type, int number, const char *message) {} void S9xParsePortConfig(ConfigFile &conf, int pass) {} bool8 S9xMapInput(const char *n, s9xcommand_t *cmd) { int i, j, d; char *c; char buf[4] = "M1+"; if (!strncmp(n, "PseudoPointer", 13) && n[13] >= '1' && n[13] <= '8' && n[14] == '\0') return (S9xMapPointer(PseudoPointerBase + (n[13] - '1'), *cmd, false)); if (!strncmp(n, "PseudoButton", 12) && isdigit(n[12]) && (j = strtol(n + 12, &c, 10)) < 256 && (c == NULL || *c == '\0')) return (S9xMapButton(PseudoButtonBase + j, *cmd, false)); if (n[0] != 'J' || !isdigit(n[1]) || !isdigit(n[2]) || n[3] != ':') return false; d = ((n[1] - '0') * 10 + (n[2] - '0')) << 24; d |= 0x80000000; i = 4; if (!strncmp(n + i, "X+", 2)) { d |= 0x4000; i += 2; } else { for (buf[1] = '1'; buf[1] <= '8'; buf[1]++) { if (!strncmp(n + i, buf, 3)) { d |= 1 << (buf[1] - '1' + 16); i += 3; } } } if (!strncmp(n + i, "Axis", 4)) { d |= 0x8000; i += 4; } else if (n[i] == 'B') i++; else return false; d |= j = strtol(n + i, &c, 10); if ((c != NULL && *c != '\0') || j > 0x3fff) return false; if (d & 0x8000) return (S9xMapAxis(d, *cmd, false)); return (S9xMapButton(d, *cmd, false)); } void InitJoysticks(void) { int version; unsigned char axes, buttons; if ((js_fd[0] = open(js_device[0], O_RDONLY | O_NONBLOCK)) == -1) { fprintf(stderr, "joystick: No joystick found.\n"); return; } if (ioctl(js_fd[0], JSIOCGVERSION, &version) == -1) { fprintf(stderr, "joystick: You need at least driver version 1.0 for joystick " "support.\n"); close(js_fd[0]); return; } for (int i = 1; i < 8; i++) js_fd[i] = open(js_device[i], O_RDONLY | O_NONBLOCK); char name[130]; bzero(name, 128); if (ioctl(js_fd[0], JSIOCGNAME(128), name) > 0) { printf("Using %s (%s) as joystick1\n", name, js_device[0]); for (int i = 1; i < 8; i++) { if (js_fd[i] > 0) { ioctl(js_fd[i], JSIOCGNAME(128), name); printf(" and %s (%s) as joystick%d\n", name, js_device[i], i + 1); } } } else { ioctl(js_fd[0], JSIOCGAXES, &axes); ioctl(js_fd[0], JSIOCGBUTTONS, &buttons); printf("Using %d-axis %d-button joystick (%s) as joystick1\n", axes, buttons, js_device[0]); for (int i = 1; i < 8; i++) { if (js_fd[i] > 0) { ioctl(js_fd[i], JSIOCGAXES, &axes); ioctl(js_fd[i], JSIOCGBUTTONS, &buttons); printf(" and %d-axis %d-button joystick (%s) as joystick%d\n", axes, buttons, js_device[i], i + 1); } } } } bool8 ReadJoysticks(void) { // track if ANY joystick event happened this frame int js_latch = FALSE; struct js_event js_ev; for (int i = 0; i < 8; i++) { // Try to reopen unplugged sticks if (js_unplugged[i]) { js_fd[i] = open(js_device[i], O_RDONLY | O_NONBLOCK); if (js_fd[i] >= 0) { fprintf(stderr, "Joystick %d reconnected.\n", i); js_unplugged[i] = FALSE; js_latch = TRUE; } } // skip sticks without valid file desc if (js_fd[i] < 0) continue; while (read(js_fd[i], &js_ev, sizeof(struct js_event)) == sizeof(struct js_event)) { switch (js_ev.type) { case JS_EVENT_AXIS: S9xReportAxis(0x8000c000 | (i << 24) | js_ev.number, js_ev.value); S9xReportAxis(0x80008000 | (i << 24) | (js_mod[i] << 16) | js_ev.number, js_ev.value); js_latch = TRUE; break; case JS_EVENT_BUTTON: case JS_EVENT_BUTTON | JS_EVENT_INIT: S9xReportButton(0x80004000 | (i << 24) | js_ev.number, js_ev.value); S9xReportButton(0x80000000 | (i << 24) | (js_mod[i] << 16) | js_ev.number, js_ev.value); js_latch = TRUE; break; } } /* EAGAIN is returned when the queue is empty */ if (errno != EAGAIN) { // Error reading joystick. fprintf(stderr, "Error reading joystick %d!\n", i); // Mark for reconnect attempt. js_unplugged[i] = TRUE; for (unsigned int j = 0; j < 16; j++) { // Center all axis S9xReportAxis(0x8000c000 | (i << 24) | j, 0); S9xReportAxis(0x80008000 | (i << 24) | (js_mod[i] << 16) | j, 0); // Unpress all buttons. S9xReportButton(0x80004000 | (i << 24) | j, 0); S9xReportButton(0x80000000 | (i << 24) | (js_mod[i] << 16) | j, 0); } js_latch = TRUE; } } return js_latch; } const char *S9xStringInput(const char *message) { static char buffer[256]; printf("%s: ", message); fflush(stdout); if (fgets(buffer, sizeof(buffer) - 2, stdin)) return (buffer); return (NULL); } bool8 S9xContinueUpdate(int width, int height) { return true; } bool S9xPollButton(uint32 id, bool *pressed) { return false; } bool S9xPollAxis(uint32 id, int16 *value) { return false; } bool8 S9xOpenSnapshotFile(const char *filepath, bool8 read_only, STREAM *file) { return true; } bool8 S9xInitUpdate(void) { return true; } bool8 S9xDeinitUpdate(int width, int height) { TDE2_RECT_S stSrcRect, stDstRect, stScaleRect; TDE2_SURFACE_S stSrc, stDst, stScale; TDE2_OPT_S stOpt = {TDE2_ALUCMD_NONE}; TDE_HANDLE s32Handle; HI_S32 s32Ret; /* 1. start job */ s32Handle = HI_TDE2_BeginJob(); if (HI_ERR_TDE_INVALID_HANDLE == s32Handle) { SAMPLE_PRT("start job failed!\n"); return false; } /* copy (scale and pixel convert) from snes buffer to scale buffer... */ stSrcRect.s32Xpos = 0; stSrcRect.s32Ypos = 0; stSrcRect.u32Height = SNES_HEIGHT; stSrcRect.u32Width = SNES_WIDTH; stSrc.enColorFmt = TDE2_COLOR_FMT_RGB565; stSrc.u32Width = SNES_WIDTH; stSrc.u32Height = SNES_HEIGHT; stSrc.u32Stride = 2 * SNES_WIDTH; stSrc.u32PhyAddr = g_pSnesBackBufferPhys; stScaleRect.s32Xpos = 0; stScaleRect.s32Ypos = 0; stScaleRect.u32Height = SCALE_HEIGHT; stScaleRect.u32Width = SCALE_WIDTH; stScale.enColorFmt = TDE2_COLOR_FMT_ARGB1555; stScale.u32Width = SCALE_WIDTH; stScale.u32Height = SCALE_HEIGHT; stScale.u32Stride = SCALE_WIDTH * 2; stScale.u32PhyAddr = g_pScaleBufferPhys; stDstRect.s32Xpos = (SCREEN_WIDTH - SCALE_WIDTH) / 2; stDstRect.s32Ypos = 0; stDstRect.u32Height = SCREEN_HEIGHT; stDstRect.u32Width = SCREEN_WIDTH; stDst.enColorFmt = TDE2_COLOR_FMT_ARGB1555; stDst.u32Width = SCREEN_WIDTH; stDst.u32Height = SCREEN_HEIGHT; stDst.u32Stride = SCREEN_WIDTH * 2; stDst.u32PhyAddr = g_stCanvasBuf.stCanvas.u32PhyAddr; stOpt.bResize = HI_TRUE; s32Ret = HI_TDE2_Bitblit(s32Handle, &stScale, &stScaleRect, &stSrc, &stSrcRect, &stScale, &stScaleRect, &stOpt); // if the screen is black all pixel values will be 0. this will cause bitblit // to fail because it doesnt understand pixel conversions with pixels that are all 0... // so i just make R = 1, G = 1, and B = 1... simple fix... lol... if (s32Ret < 0) { for (int i = 0; i < SNES_WIDTH * SNES_HEIGHT; ++i) { if (*(((uint16_t *)g_pSnesBackBufferVirt) + i) == NULL) { *(((uint16_t *)g_pSnesBackBufferVirt) + i) = BUILD_PIXEL2_RGB565(1, 1, 1); } } s32Ret = HI_TDE2_Bitblit(s32Handle, &stScale, &stScaleRect, &stSrc, &stSrcRect, &stScale, &stScaleRect, &stOpt); if (s32Ret < 0) { SAMPLE_PRT("HI_TDE2_Bitblit:%d failed,ret=0x%x!\n", __LINE__, s32Ret); HI_TDE2_CancelJob(s32Handle); return false; } } s32Ret = HI_TDE2_QuickCopy(s32Handle, &stScale, &stScaleRect, &stDst, &stDstRect); if (s32Ret < 0) { SAMPLE_PRT("HI_TDE2_QuickCopy:%d failed,ret=0x%x!\n", __LINE__, s32Ret); HI_TDE2_CancelJob(s32Handle); return false; } /* 3. submit job */ s32Ret = HI_TDE2_EndJob(s32Handle, HI_FALSE, HI_TRUE, 10); if (s32Ret < 0) { SAMPLE_PRT("Line:%d,HI_TDE2_EndJob failed,ret=0x%x!\n", __LINE__, s32Ret); HI_TDE2_CancelJob(s32Handle); return false; } HI_TDE2_WaitAllDone(); RefreshScreen(); return true; } bool8 S9xOpenSoundDevice(void) { return false; } const char *S9xGetFilename(const char *ex, s9x_getdirtype dirtype) { static char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; _splitpath(Memory.ROMFilename, drive, dir, fname, ext); snprintf(s, PATH_MAX + 1, "%s%s%s%s", S9xGetDirectory(dirtype), SLASH_STR, fname, ex); return (s); } const char *S9xGetFilenameInc(const char *ex, enum s9x_getdirtype dirtype) { static char s[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1]; unsigned int i = 0; const char *d; struct stat buf; _splitpath(Memory.ROMFilename, drive, dir, fname, ext); d = S9xGetDirectory(dirtype); do snprintf(s, PATH_MAX + 1, "%s%s%s.%03d%s", d, SLASH_STR, fname, i++, ex); while (stat(s, &buf) == 0 && i < 1000); return (s); } const char *S9xGetDirectory(enum s9x_getdirtype dirtype) { static char s[PATH_MAX + 1]; switch (dirtype) { case DEFAULT_DIR: strncpy(s, getenv("HOME"), PATH_MAX + 1); s[PATH_MAX] = 0; break; case HOME_DIR: strncpy(s, getenv("HOME"), PATH_MAX + 1); s[PATH_MAX] = 0; break; case ROMFILENAME_DIR: strncpy(s, Memory.ROMFilename, PATH_MAX + 1); s[PATH_MAX] = 0; for (int i = strlen(s); i >= 0; i--) { if (s[i] == SLASH_CHAR) { s[i] = 0; break; } } break; default: s[0] = 0; break; } return (s); } const char *S9xBasename(const char *path) { const char *p; if ((p = strrchr(path, '/')) != NULL || (p = strrchr(path, '\\')) != NULL) return (p + 1); return (path); } int main(int argc, char *argv[]) { HI_MPI_SYS_MmzAlloc(&g_pScaleBufferPhys, &g_pScaleBufferVirt, NULL, NULL, 2 * SCALE_WIDTH * SCALE_HEIGHT); HI_MPI_SYS_MmzAlloc(&g_pSnesBackBufferPhys, &g_pSnesBackBufferVirt, NULL, NULL, 2 * SNES_WIDTH * SNES_HEIGHT); GFX.Screen = (uint16_t *)g_pSnesBackBufferVirt; GFX.Pitch = 2 * SNES_WIDTH; std::printf("[+] init back buffer and scale buffer...\n"); S9xLoadConfigFiles(argv, argc); Memory.Init(); S9xInitAPU(); S9xInitSound(0); S9xSetSoundMute(TRUE); S9xGraphicsInit(); S9xBlitFilterInit(); S9xBlit2xSaIFilterInit(); S9xBlitHQ2xFilterInit(); HiInitDisplay(); std::printf("[+] init snes emulator...\n"); Memory.LoadROM("/mnt/usb-drive/super_mario.smc"); InitJoysticks(); S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0); S9xSetController(1, CTL_JOYPAD, 1, 0, 0, 0); S9xUnmapAllControls(); MAP_KEY("J00:Axis0", "Joypad1 Axis Left/Right T=50%"); MAP_KEY("J00:Axis1", "Joypad1 Axis Up/Down T=50%"); MAP_KEY("J00:B0", "Joypad1 X"); MAP_KEY("J00:B1", "Joypad1 A"); MAP_KEY("J00:B2", "Joypad1 B"); MAP_KEY("J00:B3", "Joypad1 Y"); MAP_KEY("J00:B6", "Joypad1 L"); MAP_KEY("J00:B7", "Joypad1 R"); MAP_KEY("J00:B8", "Joypad1 Select"); MAP_KEY("J00:B11", "Joypad1 Start"); std::printf("[+] setup joystick... emulating...\n"); while (true) { ReadJoysticks(); S9xMainLoop(); } }