/* 3dsspec - Nintendo 3DS Spectrum emulator. Copyright (C) 2021 Ian Cowburn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see $Id: main.c 77 2010-11-23 08:10:25Z ianc $ */ #include #include #include #include #include <3ds.h> #include "framebuffer.h" #include "gui.h" #include "keyboard.h" #include "z80.h" #include "spec.h" #include "config.h" #include "snapshot.h" #include "snap.h" #include "debug.h" #define DSSPEC_VERSION "0.1" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define DEBUG(x) \ do \ { \ printf(x); \ gfxFlushBuffers(); \ gfxSwapBuffers(); \ } while(0); \ do \ { \ hidScanInput(); \ } while(!(hidKeysDown() & KEY_TOUCH)) \ /* ---------------------------------------- STATIC DATA */ static const char *main_menu[]= { "Reset Spectrum", "Load TAP File", "Configure", "Map Joypad to Keys", "Save Memory Snapshot", "Load Memory Snapshot", "Save Joypad/Key State", "Load Joypad/Key State", "Enable Debug", "Exit 3DSSPEC", "Cancel", NULL }; typedef enum { MenuReset, MenuLoadTAP, MenuConfigure, MenuMapJoypad, MenuSaveSnapshot, MenuLoadSnapshot, MenuSaveMappings, MenuLoadMappings, MenuDebug, MenuExit } MenuOpt; /* ---------------------------------------- DISPLAY FUNCS */ static void Splash(void) { static char scroller[]= { " " "Welcome to 3DSSPEC, a Sinclair Spectrum 48K emulator for the Nintendo " "3DS. You can safely ignore this message. I was just bored for half " "an hour. And no retro game is complete without a side-scroller... " "Thanks to Slay Radio, Ladytron, the Genki Rockets, the High " "Voltage SID Collection, The Prodigy, Paradise Lost and " "Retro Gamer for coding fuel." }; static const char *text[]= { "3DSSPEC \177 2021 Ian Cowburn", " ", "Spectrum ROM \177 1982 Amstrad", " ", "PRESS A TO CONTINUE", " ", "https://deathstation9000.org.uk/", " ", NULL }; int f; int y; int scr_x = 0; int scr_y = 240; Framebuffer upper; Framebuffer lower; SNAP_Enable(TRUE); while(!(hidKeysDown() & KEY_A)) { FB_StartFrame(&upper, &lower); FB_Clear(&upper, COL_BLACK); FB_Clear(&lower, COL_BLACK); FB_Blit(&upper, IMG_SPLASH, 0, scr_y); FB_Print(&upper, "10 REM VERSION \001" DSSPEC_VERSION "\001\n" "20 PRINT \"\001THE SPECTRUM IS ACE\001\"\n" "30 GOTO 20", 0, 230, COL_BLACK, COL_TRANSPARENT); FB_printf(&lower, scr_x, 8, COL_WHITE, COL_TRANSPARENT, "%-42.42s",scroller); y = lower.height - 20; for(f=0;text[f];f++) { FB_Centre(&lower, text[f], y, COL_WHITE, COL_TRANSPARENT); y -= 8; } FB_EndFrame(); if (scr_y > 0) { scr_y--; } if (--scr_x == -8) { size_t l = sizeof scroller; char c; scr_x = 0; c = scroller[0]; memmove(scroller, scroller+1, l-2); scroller[l-2] = c; } } } /* ---------------------------------------- JOYPAD MAPPING */ static void MapJoypad(void) { SoftKeyEvent ev; SoftKey pad = NUM_SOFT_KEYS; int done = FALSE; Framebuffer upper; Framebuffer lower; while(!done) { FB_StartFrame(&upper, &lower); SK_DisplayKeyboard(); FB_Clear(&upper, COL_WHITE); FB_Print(&upper, "Press the joypad button you want\n" "to define and then the Spectrum key\n" "you want to use for that button.\n\n" "Press MENU to finish.", 0, upper.height - 40, COL_BLACK,COL_TRANSPARENT); if (pad != NUM_SOFT_KEYS) { FB_printf(&upper, 0, 80, COL_BLACK, COL_TRANSPARENT, "defining\n \001%s\001",SK_KeyName(pad)); } FB_EndFrame(); while(SK_GetBareEvent(&ev)) { if (ev.pressed) { if (ev.key==SK_ABOUT || ev.key==SK_CONFIG) { done = true; } } else { if (ev.key>=SK_PAD_UP && ev.key<=SK_PAD_SELECT) { pad = ev.key; } if (ev.key<=SK_SPACE && pad!=NUM_SOFT_KEYS) { SK_DefinePad(pad,ev.key); pad = NUM_SOFT_KEYS; } } } } } /* ---------------------------------------- MAIN */ int main(int argc, char *argv[]) { Z80 *z80; int quit = FALSE; float mix[12] = {1.0, 1.0}; u32 *SOC_buffer; int soc_initialised = FALSE; gfxInit(GSP_RGB565_OES, GSP_RGB565_OES, FALSE); ndspInit(); ndspSetOutputMode(NDSP_OUTPUT_MONO); ndspChnSetInterp(0, NDSP_INTERP_NONE); ndspChnSetRate(0, SAMPLE_RATE); ndspChnSetFormat(0, NDSP_FORMAT_MONO_PCM8); ndspChnSetMix(0, mix); FB_Init(); GUI_Init(); SOC_buffer = (u32*)memalign(0x1000,0x100000); if (SOC_buffer) { int ret; if ((ret = socInit(SOC_buffer, 0x100000)) == 0) { soc_initialised = TRUE; } else { char msg[256]; snprintf(msg, sizeof msg, "Failed to initialise SOC\n0x%08X", (unsigned int)ret); GUI_Alert(FALSE, msg); } } else { GUI_Alert(FALSE, "Failed to allocate memory for SOC!"); } z80 = Z80Init(SPECReadPort, SPECWritePort); if (!z80) { GUI_Alert(TRUE,"Failed to initialise\nthe Z80 CPU emulation!"); } SPECInit(z80); Splash(); LoadConfig(); SPECReconfigure(); SK_DisplayKeyboard(); SK_SetSticky(SK_SHIFT,DSSPEC_Config[DSSPEC_STICKY_SHIFT]); SK_SetSticky(SK_SYMBOL,DSSPEC_Config[DSSPEC_STICKY_SHIFT]); if (DSSPEC_Config[DSSPEC_LOAD_DEFAULT_SNAPSHOT]) { SNAP_Load(z80, "AUTO", SNAP_TYPE_FULL); } while(!quit && aptMainLoop()) { SoftKeyEvent ev; Framebuffer upper; Framebuffer lower; FB_StartFrame(&upper, &lower); SK_DisplayKeyboard(); SPECStartFrame(&upper); Z80Exec(z80); FB_EndFrame(); while(SK_GetEvent(&ev)) { switch(ev.key) { case SK_ABOUT: case SK_CONFIG: if (ev.pressed) { switch(GUI_Menu(main_menu)) { case MenuReset: SPECReset(z80); break; case MenuLoadTAP: { char file[FILENAME_MAX]; if (GUI_FileSelect(last_dir, file, ".TAP")) { TAPCloseTape(); TAPOpenTape(file); } break; } case MenuConfigure: GUI_Config(); SK_SetSticky (SK_SHIFT, DSSPEC_Config[DSSPEC_STICKY_SHIFT]); SK_SetSticky (SK_SYMBOL, DSSPEC_Config[DSSPEC_STICKY_SHIFT]); SPECReconfigure(); break; case MenuMapJoypad: MapJoypad(); break; case MenuSaveSnapshot: SNAP_Save(z80, SNAP_TYPE_FULL); break; case MenuLoadSnapshot: SNAP_Load(z80, NULL, SNAP_TYPE_FULL); break; case MenuSaveMappings: SNAP_Save(z80, SNAP_TYPE_KEYBOARD); break; case MenuLoadMappings: SNAP_Load(z80, NULL, SNAP_TYPE_KEYBOARD); break; case MenuDebug: if (soc_initialised) { char host[32] = {0}; char port[32] = {0}; debug_enabled = TRUE; GUI_Input("Enter host", host, 31); GUI_Input("Enter port", port, 31); if (!host[0]) { strcpy(host, "192.168.0.77"); } if (!port[0]) { strcpy(port, "12345"); } DEBUG_SetAddress(host, port); } break; case MenuExit: quit = TRUE; break; } } break; default: SPECHandleKey(ev.key,ev.pressed); break; } } } ndspExit(); gfxExit(); if (soc_initialised) { socExit(); } return 0; }