/* 3ds81 - Nintendo 3DS ZX81 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 <3ds.h> #include "framebuffer.h" #include "gui.h" #include "keyboard.h" #include "z80.h" #include "zx81.h" #include "tapes.h" #include "config.h" #include "snapshot.h" #ifndef DS81_VERSION #define DS81_VERSION "DEV " __TIME__ "/" __DATE__ #endif #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 ZX81", "Select Tape", "Configure", "Map Joypad to Keys", "Save Memory Snapshot", "Load Memory Snapshot", "Save Joypad/Key State", "Load Joypad/Key State", "Help", "Exit 3DS81", "Cancel", NULL }; typedef enum { MenuReset, MenuSelectTape, MenuConfigure, MenuMapJoypad, MenuSaveSnapshot, MenuLoadSnapshot, MenuSaveMappings, MenuLoadMappings, MenuHelp, MenuExit } MenuOpt; /* ---------------------------------------- DISPLAY FUNCS */ static void Splash(void) { static char scroller[]= { " " "Welcome to 3DS81, a ZX81 emulator for the Ninetendo 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[]= { "3DS81 \177 2021 Ian Cowburn", " ", "ZX81 ROM \177 1981", "Nine Tiles Networks LTD", " ", "PRESS A TO CONTINUE", " ", "https://noddybox.co.uk/", " ", "If you place .P tape files in", "the top directory or ZX81SNAP", "then you should be able to load", "GAME.P with the command", "LOAD \"GAME\"", NULL }; int f; int y; int scr_x = 0; int scr_y = 240; Framebuffer upper; Framebuffer lower; ZX81EnableFileSystem(TRUE); 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" DS81_VERSION "\001\n" "20 PRINT \"\001THE ZX81 IS ACE\001\"\n" "30 GOTO 20", 0, 230, COL_WHITE, 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; } } } /* ---------------------------------------- HELP */ static void ShowHelp(void) { struct { FB_Colour col; const char *text; } help[] = { {COL_RED, "KEYBOARD"}, {COL_BLACK, "Use the touchscreen to press keys on the computer."}, {COL_BLACK, "The ZX81 had a keyword entry system, so when the"}, {COL_BLACK, "cursor is an inverse K then the keyword printed"}, {COL_BLACK, "at the top of the key is displayed. Therefore"}, {COL_BLACK, "to load a tape press the J key, then the SHIFT"}, {COL_BLACK, "key (which by default stays pressed until you"}, {COL_BLACK, "click it again) and press the P key to enter a"}, {COL_BLACK, "quote, press the SHIFT key to stop pressing it,"}, {COL_BLACK, "type the name of the .P file and close it with"}, {COL_BLACK, "another quote, then press ENTER"}, {COL_TRANSPARENT, ""}, {COL_RED, "LOADING TAPES"}, {COL_BLACK, "There are two ways of loading tapes in the"}, {COL_BLACK, "simulator. The first is to put .P files in"}, {COL_BLACK, "either the root directory of the SD card"}, {COL_BLACK, "or a directory called ZX81SNAP. Files from"}, {COL_BLACK, "either place can be loaded by typing"}, {COL_BLACK, "LOAD \"GAME\" to load GAME.P. Alternatively"}, {COL_BLACK, "you can type LOAD \"*\" to display a file"}, {COL_BLACK, "selector that allows you to select the tape"}, {COL_BLACK, "to load. LOAD \"\" can be used to load an"}, {COL_BLACK, "internal tape image once it has been selected."}, {COL_TRANSPARENT, NULL} }; int done = FALSE; u32 key; Framebuffer upper; Framebuffer lower; while(!done) { int f; FB_StartFrame(&upper, &lower); FB_Clear(&upper, COL_WHITE); FB_Clear(&lower, COL_BLACK); for(f = 0; help[f].text; f++) { FB_Print(&upper, help[f].text, 0, upper.height - f * 8, help[f].col,COL_TRANSPARENT); } FB_Print(&lower, "Scroll around help by using the\n" "control pad\n" "Press A to finish.", 0, lower.height - 40, COL_YELLOW,COL_TRANSPARENT); FB_EndFrame(); key = hidKeysDown(); if (key & KEY_A) { done = TRUE; } } } /* ---------------------------------------- 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 ZX81 key\n" "you want to use.\n\n" "Press CONFIG 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; gfxInit(GSP_RGB565_OES, GSP_RGB565_OES, false); FB_Init(); z80 = Z80Init(ZX81ReadMem, ZX81WriteMem, ZX81ReadPort, ZX81WritePort, ZX81ReadDisassem); if (!z80) { GUI_Alert(TRUE,"Failed to initialise\nthe Z80 CPU emulation!"); } ZX81Init(z80); Splash(); LoadConfig(); ZX81Reconfigure(); SK_DisplayKeyboard(); SK_SetSticky(SK_SHIFT,DS81_Config[DS81_STICKY_SHIFT]); if (DS81_Config[DS81_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(); Z80Exec(z80); ZX81RenderDisplay(&upper, 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: ZX81Reset(z80); break; case MenuSelectTape: SelectTape(); break; case MenuConfigure: GUI_Config(); SK_SetSticky(SK_SHIFT, DS81_Config[DS81_STICKY_SHIFT]); ZX81Reconfigure(); 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 MenuHelp: ShowHelp(); break; case MenuExit: quit = TRUE; break; } } break; default: ZX81HandleKey(ev.key,ev.pressed); break; } } } gfxExit(); return 0; }