From c3b282d6dbb1d17b289aec18daad1739cdc02b63 Mon Sep 17 00:00:00 2001 From: ammeir <62232971+ammeir@users.noreply.github.com> Date: Wed, 18 Mar 2020 21:33:44 +0200 Subject: [PATCH] First commit. --- src/arch/psvita/controller/controller.cpp | 1581 +++++++++++++++++++++ src/arch/psvita/controller/controller.h | 149 ++ src/arch/psvita/controller/ctrl_defs.h | 32 + 3 files changed, 1762 insertions(+) create mode 100644 src/arch/psvita/controller/controller.cpp create mode 100644 src/arch/psvita/controller/controller.h create mode 100644 src/arch/psvita/controller/ctrl_defs.h diff --git a/src/arch/psvita/controller/controller.cpp b/src/arch/psvita/controller/controller.cpp new file mode 100644 index 0000000..4de9271 --- /dev/null +++ b/src/arch/psvita/controller/controller.cpp @@ -0,0 +1,1581 @@ + +/* controller.cpp: Purpose of the controller is to act as a middle man + between View and Model (VICE). + + Copyright (C) 2019-2020 Amnon-Dan Meir. + + 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 . + + Author contact information: + Email: ammeir71@yahoo.com +*/ + +#include "controller.h" +#include "control_pad.h" +#include "guitools.h" +#include "ctrl_defs.h" +#include "app_defs.h" +#include "debug_psv.h" +#include "resid/sid.h" + +// C interface files. Tell g++ not to mangle symbols. +extern "C" { +#include "main.h" +#include "ui.h" +#include "autostart.h" +#include "cartridge.h" +#include "resources.h" +#include "mem.h" +#include "video.h" +#include "machine.h" +#include "sound.h" +#include "vsync.h" +#include "sid.h" +#include "joy.h" +#include "attach.h" +#include "tape.h" +#include "datasette.h" +#include "c64cartsystem.h" +#include "joystick.h" +#include "keyboard.h" +#include "log.h" +#include "videoarch.h" +#include "tapecontents.h" +#include "diskcontents.h" +#include "attach.h" +#include "lib.h" +#include "mousedrv.h" +#include "raster.h" +#include "snapshot.h" +#include "kbdbuf.h" +} + +#include +#include +#include +#include + + +static View* gs_view; + +extern "C" int PSV_CreateView(int width, int height, int depth) +{ + return gs_view->createView(width, height, depth); +} + +extern "C" void PSV_UpdateView() +{ + gs_view->updateView(); + // Avoid updating the view twice. + gs_frameDrawn = true; +} + +extern "C" void PSV_SetViewport(int x, int y, int width, int height) +{ + // Make sure we stay borderless when e.g. changing video standard. + if (gs_view->isBorderlessView()){ + video_canvas_s* canvas; + video_psv_get_canvas(&canvas); + width = canvas->geometry->gfx_size.width; + height = canvas->geometry->gfx_size.height; + x = canvas->geometry->extra_offscreen_border_left + canvas->geometry->gfx_position.x; + y = canvas->geometry->gfx_position.y; + } + + gs_view->updateViewport(x, y, width, height); +} + +extern "C" void PSV_GetViewInfo(int* width, int* height, unsigned char** ppixels, int* pitch, int* bpp) +{ + gs_view->getViewInfo(width, height, ppixels, pitch, bpp); +} + +extern "C" void PSV_ScanControls() +{ + static ControlPadMap* maps[16]; + int size = 0; + char joy_pin_mask = 0x00; + + + // Goto main menu at boot time. There must be a better place to implement this. + if (gs_bootTime){ + video_psv_menu_show(); + gs_bootTime = false; + return; + } + + gs_view->scanControls(&joy_pin_mask, maps, &size, gs_scanMouse); + + for (int i=0; iisjoystick){ + if (map->ispress) + joystick_value[g_joystickPort] |= map->joypin; + else + joystick_value[g_joystickPort] &= ~map->joypin; + continue; + } + if (map->iskey){ + // Get row and column from the mid. If bit 4 is set, negate the row value. + int row = (map->mid & 0x08)? -(map->mid >> 4): (map->mid >> 4); + int column = map->mid & 0x07; + keyboard_set_keyarr_any(row, column, map->ispress); + continue; + } + if (!map->ispress) // Ignore button releases + continue; + + // Special commands + switch (map->mid){ + case 126: // Show menu + if (ui_emulation_is_paused()){ + // We can show menu right away. No need to trigger trap if in pause state + // and the sound buffer is already silenced. + gs_view->activateMenu(); + setSoundVolume(100); + break; + } + + setPendingAction(CTRL_ACTION_SHOW_MENU); + break; + case 127: // Show/hide keyboard + gs_view->toggleKeyboardOnView(); + keyboard_clear_keymatrix(); // Remove any keys that were not released. + gs_view->updateView(); // Force draw in case of still image. + break; + case 128: // Pause/unpause + if (!ui_emulation_is_paused()) + setPendingAction(CTRL_ACTION_PAUSE); + else{ + ui_pause_emulation(0); + gs_view->displayPaused(0); + gs_view->updateView(); + setSoundVolume(100); + } + break; + case 129: // Swap joysticks + toggleJoystickPorts(); + break; + case 130: // Toggle warp mode + toggleWarpMode(); + break; + default: + break; + } + } + + checkPendingActions(); + + + // Because Vice updates the screen inconsistently, we have a problem with updating the statusbar and + // showing keyboard magnifying boxes. This seems out of place here but as the scan happens after the + // end of each frame it's a pretty good place to constantly check for something. + // gs_frameDrawn variable is used to avoid updating the view twice on one frame. + // It works because screen draw is allways followed by the scan. + if (!gs_frameDrawn && gs_view->pendingRedraw()) + gs_view->updateView(); + + gs_frameDrawn = false; +} + +extern "C" void PSV_ApplySettings() +{ + // Disable CRT emulation. + resources_set_int(VICE_RES_VICII_FILTER, 0); + + // Enable general mechanisms for fast disk/tape emulation. + resources_set_int(VICE_RES_VIRTUAL_DEVICES, 1); + + // This is very important for ReSID performance. + // Any other value brings the emulation to almost complete standstill. + resources_set_int(VICE_RES_SID_RESID_SAMPLING,0); + + // Apply all user defined settings. + gs_view->applyAllSettings(); +} + +extern "C" void PSV_ActivateMenu() +{ + gs_view->activateMenu(); +} + +extern "C" int PSV_RGBToPixel(uint8_t r, uint8_t g, uint8_t b) +{ + return gs_view->convertRGBToPixel(r,g,b); +} + +extern "C" void PSV_NotifyPalette(unsigned char* palette, int size) +{ + gs_view->setPalette(palette, size); +} + +extern "C" void PSV_NotifyFPS(int fps, float percent, int warp_flag) +{ + gs_view->setFPSCount(fps, (int)percent, warp_flag); +} + +extern "C" void PSV_NotifyTapeCounter(int counter) +{ + gs_view->setTapeCounter(counter); +} + +extern "C" void PSV_NotifyTapeControl(int control) +{ + gs_view->setTapeControl(control); +} + +extern "C" int PSV_ShowMessage(const char* msg, int msg_type) +{ + return gs_view->showMessage(msg, msg_type); +} + +extern "C" void PSV_NotifyReset() +{ + gs_view->notifyReset(); +} + +Controller::Controller() +{ + +} + +Controller::~Controller() +{ +} + +void Controller::init(View* view) +{ + gs_view = view; +} + +int Controller::loadFile(load_type_e load_type, const char* file, int index, const char* target_file) +{ + if (!file) + return -1; + + int ret = 0; + + switch (load_type){ + case CART_LOAD: + case AUTO_DETECT_LOAD: + { + int device = getImageType(file); + + // Remove any attached cartridge or it will be loaded instead. + cartridge_detach_image(-1); + // Sometimes a tape won't run without reseting the datasette. + datasette_control(DATASETTE_CONTROL_RESET); + //tape_image_detach(1); + //file_system_detach_disk(8); + + // This prevents sound loss when loading game when previous load hasn't finished. + resources_set_int(VICE_RES_WARP_MODE, 0); + + // Unpause emulation. + if (ui_emulation_is_paused()){ + ui_pause_emulation(0); + gs_view->displayPaused(0); + } + + // Cartridge won't load if 'CartridgeReset' is disabled. Enable it temporarily. + int cartridge_reset = 1; + if (device == IMAGE_CARTRIDGE){ + resources_get_int(VICE_RES_CARTRIDGE_RESET, &cartridge_reset); + if (!cartridge_reset) + resources_set_int(VICE_RES_CARTRIDGE_RESET, 1); + } + + gtShowMsgBoxNoBtn("Loading..."); + + ret = autostart_autodetect(file, NULL, index, AUTOSTART_MODE_RUN); + + // Restore old value. + if (!cartridge_reset) + resources_set_int(VICE_RES_CARTRIDGE_RESET, 0); + + break; + } + case DISK_LOAD: + { + // This prevents sound loss when loading game when previous load hasn't finished. + resources_set_int(VICE_RES_WARP_MODE, 0); + + char* prog_name = NULL; + string str_prog_name; + + image_contents_t *contents = diskcontents_filesystem_read(file); + if (contents) { + prog_name = image_contents_filename_by_number(contents, index); + image_contents_destroy(contents); + } + + // Remove 0xa0 characters from file names. + if (prog_name){ + str_prog_name = prog_name; + size_t pos = str_prog_name.find_first_of(0xa0); + if (pos != string::npos){ + str_prog_name = str_prog_name.substr(0, pos); + } + lib_free(prog_name); + } + + gs_loadProgramName = !str_prog_name.empty()? str_prog_name.c_str(): "*"; + setPendingAction(CTRL_ACTION_LOAD_DISK); + break; + } + case TAPE_LOAD: + { + if (!tape_image_dev1) + return -1; + + if (index > 0) + tape_seek_to_file(tape_image_dev1, index); + + setPendingAction(CTRL_ACTION_LOAD_TAPE); + break; + } + } + + return ret; +} + +int Controller::loadState(const char* file) +{ + // Prevent sound loss when loading a state when previous load hasn't finished. + resources_set_int(VICE_RES_WARP_MODE, 0); + + //char* sfile = lib_stralloc(file); + //ui_load_snapshot(sfile); + //return 1; + + string cartridge; + const char* file_name = cartridge_get_file_name(cart_getid_slotmain()); + + if (file_name) + cartridge = file_name; + + int ret = machine_read_snapshot((char*)file, 0); + + if (!cartridge.empty()){ + // When loading a snapshot Vice detaches any attached cartridges. Reading the slot returns null + // but when doing a reset it still loads the old cartridge. Not sure if this is a bug or my missunderstanding. + // The workaround is to read the cart name before loading the snapshot and then attaching it back after. + // CartridgeReset has to be disabled or otherwise the cpu will reset. + int cartridge_reset; + resources_get_int(VICE_RES_CARTRIDGE_RESET, &cartridge_reset); + resources_set_int(VICE_RES_CARTRIDGE_RESET, 0); + cartridge_attach_image(CARTRIDGE_CRT, cartridge.c_str()); + resources_set_int(VICE_RES_CARTRIDGE_RESET, cartridge_reset); + } + + return ret; + + //return machine_read_snapshot((char*)file, 0); +} + +int Controller::saveState(const char* file_name) +{ + return machine_write_snapshot(file_name, 0, 0, 0); +} + +int Controller::patchSaveState(patch_data_s* patch) +{ + if (!patch || !patch->snapshot_file || !patch->data) + return -1; + + FILE *fp; + + if (!(fp = fopen(patch->snapshot_file, "a+"))){ + return -1; + } + + // We can't add a module to a snapshot without changing code in Vice. + // Workaround is to append a byte chunk to the snapshot file that is in right format. + // Vice will happily accept it. + + // Vice snapshot module format: + // Header: + // Name (16 bytes) + // Major version (1 byte) + // Minor version (1 byte1) + // Size of module (4 bytes) + // Data: (Any number of fields. We use two, dword and byte array) + // Data size (4 bytes) + // Data (n bytes) + + int module_size = 16 + 1 + 1 + 4 + 4 + patch->data_size; + char* buf = new char[module_size]; + + char name[16] = {0}; + + if (patch->module_name) + strncpy(name, patch->module_name, 16); + + // Header + memcpy(buf, name, 16); + buf[16] = patch->major; + buf[17] = patch->minor; + buf[18] = module_size; + buf[19] = module_size >> 8; + buf[20] = module_size >> 16; + buf[21] = module_size >> 24; + // Data + buf[22] = patch->data_size; + buf[23] = patch->data_size >> 8; + buf[24] = patch->data_size >> 16; + buf[25] = patch->data_size >> 24; + memcpy(&buf[26], patch->data, patch->data_size); + + if (fwrite(buf, 1, module_size, fp) < 1) { + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +int Controller::getSaveStatePatch(patch_data_s* pinfo) +{ + uint8_t major_version; + uint8_t minor_version; + snapshot_t* snapshot; + snapshot_module_t* patch_module; + + // Reading our patch module is best done by using the snapshot api. + // This way we don't have to remember the file offset to our data. + + // First we need to open the snapshot. + snapshot = snapshot_open(pinfo->snapshot_file, &major_version, &minor_version, machine_get_name()); + + if (!snapshot){ + return -1; + } + + // Get our patch module from the snapshot. + patch_module = snapshot_module_open(snapshot, + pinfo->module_name, + &pinfo->major, + &pinfo->minor); + + if (!patch_module){ + snapshot_close(snapshot); + return -1; + } + + // Read the byte array size. + if (snapshot_module_read_dword(patch_module, &pinfo->data_size) < 0){ + snapshot_module_close(patch_module); + snapshot_close(snapshot); + return -1; + } + + // Read the byte array. + snapshot_module_read_byte_array(patch_module, + (uint8_t*)pinfo->data, + pinfo->data_size); + + + snapshot_module_close(patch_module); + snapshot_close(snapshot); + + return 0; +} + +int Controller::getSaveStatePatchInfo(patch_data_s* pinfo) +{ + uint8_t major_version; + uint8_t minor_version; + snapshot_t* snapshot; + snapshot_module_t* patch_module; + + snapshot = snapshot_open(pinfo->snapshot_file, &major_version, &minor_version, machine_get_name()); + + if (!snapshot){ + return -1; + } + + patch_module = snapshot_module_open(snapshot,pinfo->module_name,&pinfo->major,&pinfo->minor); + + if (!patch_module){ + snapshot_close(snapshot); + return -1; + } + + // Read the byte array size. + if (snapshot_module_read_dword(patch_module, &pinfo->data_size) < 0){ + snapshot_module_close(patch_module); + snapshot_close(snapshot); + return -1; + } + + snapshot_module_close(patch_module); + snapshot_close(snapshot); + + return 0; +} + +void Controller::resetComputer() +{ + machine_trigger_reset(gs_machineResetMode); +} + +void Controller::setModelProperty(int key, const char* value) +{ + if (!key || !value) + return; + + switch (key){ + case JOYSTICK_PORT: + changeJoystickPort(value);break; + case COLOR_PALETTE: + setColorPalette(value);break; + case CPU_SPEED: + setCpuSpeed(value);break; + case SOUND: + setAudioPlayback(value);break; + case SID_ENGINE: + setSidEngine(value);break; + case SID_MODEL: + setSidModel(value);break; + case VICII_MODEL: + setViciiModel(value);break; + case DRIVE_TRUE_EMULATION: + setDriveEmulation(value);break; + case DRIVE_SOUND_EMULATION: + setDriveSoundEmulation(value);break; + case DATASETTE_RESET_WITH_CPU: + setDatasetteReset(value);break; + case CARTRIDGE_RESET: + setCartridgeReset(value);break; + case MACHINE_RESET: + setMachineResetMode(value); break; + } +} + +void Controller::getImageFileContents(int peripheral, const char* image, const char*** values, int* values_size, image_contents_t* content) +{ + *values = NULL; + *values_size = 0; + + if (peripheral == DRIVE8 || peripheral == DATASETTE){ + // Retrieve disk/tape contents + if (!content) + return; + + image_contents_file_list_t* entry = content->file_list; + + // Get list size; + int size = 0; + while (entry){ + size++; + entry = entry->next; + } + + if (!size) + return; + + char** p = new char*[size]; + *values = (const char**)p; + *values_size = size; + entry = content->file_list; + for (int i=0; inext; + p++; + } + } +} + +void Controller::changeJoystickPort(const char* port) +{ + if (!strcmp(port, "Port 1")){ + g_joystickPort = 1; + resources_set_int(VICE_RES_JOY_PORT1_DEV, 1); // 1 = Joystick + resources_set_int(VICE_RES_JOY_PORT2_DEV, 0); // 0 = None + } + else if (!strcmp(port, "Port 2")){ + g_joystickPort = 2; + resources_set_int(VICE_RES_JOY_PORT2_DEV, 1); + resources_set_int(VICE_RES_JOY_PORT1_DEV, 0); + } +} + +void Controller::setCpuSpeed(const char* val) +{ + int value; + + if(!strcmp(val, "100%")) + value = 100; + else if (!strcmp(val, "125%")) + value = 125; + else if (!strcmp(val, "150%")) + value = 150; + else if (!strcmp(val, "175%")) + value = 175; + else if (!strcmp(val, "200%")) + value = 200; + else + value = 100; + + resources_set_int(VICE_RES_CPU_SPEED, value); +} + +void Controller::setAudioPlayback(const char* val) +{ + int value = !strcmp(val, "Enabled")? 1: 0; + resources_set_int(VICE_RES_SOUND, value); +} + +void Controller::setSidEngine(const char* val) +{ + int value; + if (!strcmp(val, "FastSID")) + value = SID_ENGINE_FASTSID; + else if (!strcmp(val, "ReSID")) + value = SID_ENGINE_RESID; + else + return; + + resources_set_int(VICE_RES_SID_ENGINE, value); + +} + +void Controller::setSidModel(const char* val) +{ + int value; + if (!strcmp(val, "6581")) + value = SID_MODEL_6581; + else if (!strcmp(val, "8580")) + value = SID_MODEL_8580; + else + return; + + resources_set_int(VICE_RES_SID_MODEL, value); +} + +void Controller::setBorderVisibility(const char* val) +{ + // We show/hide borders by changing the viewport size. This is much more user friendly than + // changing the VICIIBorderMode resource which will result in a computer reset. + + int x,y,width,height; + struct video_canvas_s* canvas; + + video_psv_get_canvas(&canvas); + + if (!strcmp(val,"Hide")){ + width = canvas->geometry->gfx_size.width; + height = canvas->geometry->gfx_size.height; + x = canvas->geometry->extra_offscreen_border_left + canvas->geometry->gfx_position.x; + y = canvas->geometry->gfx_position.y; + } + else{ + width = canvas->draw_buffer->canvas_width; + height = canvas->draw_buffer->canvas_height; + x = canvas->geometry->extra_offscreen_border_left; + y = canvas->geometry->first_displayed_line; + } + + gs_view->updateViewport(x,y,width,height); +} + +/* +Removing borders by changing the viewport has one problem. It sometimes shows visual artifacts on +screen edges. This happens seldomly and can be prevented by changing the texture filtering value to 'Point'. +This version can also remove the borders by changing the VICIIBorderMode resource. +Commented out because lack of testing. +*/ +//void Controller::setBorderVisibility(const char* val) +//{ +// int value; +// if (resources_get_int("VICIIBorderMode", &value) < 0) +// return; +// +// int x,y,width,height; +// struct video_canvas_s* canvas; +// +// video_psv_get_canvas(&canvas); +// +// if (value == 0/*VICII_NORMAL_BORDERS*/){ +// if (!strcmp(val,"Hide")){ +// width = canvas->geometry->gfx_size.width;// - 2; +// height = canvas->geometry->gfx_size.height;// - 2; +// x = canvas->geometry->extra_offscreen_border_left + canvas->geometry->gfx_position.x;// + 1; +// y = canvas->geometry->gfx_position.y;// + 1; +// } +// else if (!strcmp(val,"Show")){ +// width = canvas->draw_buffer->canvas_width; +// height = canvas->draw_buffer->canvas_height; +// x = canvas->geometry->extra_offscreen_border_left; +// y = canvas->geometry->first_displayed_line; +// } +// else if (!strcmp(val,"Remove")){ +// resources_set_int("VICIIBorderMode", 3/*VICII_NO_BORDERS*/); +// return; +// } +// +// gs_view->updateViewport(x,y,width,height); +// } +// else if (value == 3/*VICII_NO_BORDERS*/){ +// if (!strcmp(val,"Show")){ +// resources_set_int("VICIIBorderMode", 0/*VICII_NORMAL_BORDERS*/); +// } +// } +//} + +void Controller::setViciiModel(const char* val) +{ + int value = MACHINE_SYNC_PAL; + + if (!strcmp(val, "NTSC")) + value = MACHINE_SYNC_NTSC; + else if (!strcmp(val, "Old NTSC")) + value = MACHINE_SYNC_NTSCOLD; + else if (!strcmp(val, "PAL-N")) + value = MACHINE_SYNC_PALN; + + resources_set_int(VICE_RES_MACHINE_VIDEO_STANDARD, value); +} + +void Controller::setCrtEmulation() +{ + resources_set_int(VICE_RES_VICII_DOUBLE_SCAN, 1); + resources_set_int(VICE_RES_VICII_DOUBLE_SIZE, 1); + // Crt emulation (scan lines) 0=off, 1=on + resources_set_int(VICE_RES_VICII_FILTER, 1); +} + +void Controller::setColorPalette(const char* val) +{ + const char* curr_palette; + const char* new_palette = NULL; + + resources_get_string_sprintf("%sPaletteFile", &curr_palette, "VICII"); + if (!curr_palette) curr_palette = ""; + + if (!strcmp(val, "Pepto (PAL)") && strcmp("pepto-pal", curr_palette)) + new_palette = "pepto-pal"; + else if (!strcmp(val, "Colodore") && strcmp("colodore", curr_palette)) + new_palette = "colodore"; + else if (!strcmp(val, "Vice") && strcmp("vice", curr_palette)) + new_palette = "vice"; + else if (!strcmp(val, "Ptoing") && strcmp("ptoing", curr_palette)) + new_palette = "ptoing"; + else if (!strcmp(val, "RGB") && strcmp("rgb", curr_palette)) + new_palette = "rgb"; + else if (!strcmp(val, "None")) + new_palette = "None"; + + if (new_palette){ + if (strcmp(val, "None")){ + resources_set_int(VICE_RES_VICII_EXTERNAL_PALETTE, 1); + resources_set_string_sprintf("%sPaletteFile", new_palette, "VICII"); + } + else{ + resources_set_int(VICE_RES_VICII_EXTERNAL_PALETTE, 0); + } + + updatePalette(); + } +} + +void Controller::setDriveEmulation(const char* val) +{ + // Enable/Disable hardware-level emulation of disk drives. + if (!strcmp(val, "Fast")) + resources_set_int(VICE_RES_DRIVE_TRUE_EMULATION, 0); + else if (!strcmp(val, "True")) + resources_set_int(VICE_RES_DRIVE_TRUE_EMULATION, 1); +} + +void Controller::setDriveSoundEmulation(const char* val) +{ + // Enable/Disable sound emulation of disk drives. + if (!strcmp(val, "Disabled")) + resources_set_int(VICE_RES_DRIVE_SOUND_EMULATION, 0); + else if (!strcmp(val, "Enabled")) + resources_set_int(VICE_RES_DRIVE_SOUND_EMULATION, 1); +} + +void Controller::setJoystickPort(const char* val) +{ + if (!strcmp(val, "Port 1")){ + resources_set_int(VICE_RES_JOY_DEVICE_1, JOYDEV_JOYSTICK); + resources_set_int(VICE_RES_JOY_DEVICE_2, JOYDEV_NONE); + } + else if (!strcmp(val, "Port 2")){ + resources_set_int(VICE_RES_JOY_DEVICE_1, JOYDEV_NONE); + resources_set_int(VICE_RES_JOY_DEVICE_2, JOYDEV_JOYSTICK); + } +} + +void Controller::setDatasetteReset(const char* val) +{ + // Enable/Disable automatic Datasette-Reset. + if (!strcmp(val, "Enabled")) + resources_set_int(VICE_RES_DATASETTE_RESET_WITH_CPU, 1); + else if (!strcmp(val, "Disabled")) + resources_set_int(VICE_RES_DATASETTE_RESET_WITH_CPU, 0); +} + +void Controller::setCartridgeReset(const char* val) +{ + // Reset machine if a cartridge is attached or detached + if (!strcmp(val, "Enabled")) + resources_set_int(VICE_RES_CARTRIDGE_RESET, 1); + else if (!strcmp(val, "Disabled")) + resources_set_int(VICE_RES_CARTRIDGE_RESET, 0); +} + +void Controller::setMachineResetMode(const char* val) +{ + if (!strcmp(val, "Hard")) + gs_machineResetMode = MACHINE_RESET_MODE_HARD; + else if (!strcmp(val, "Soft")) + gs_machineResetMode = MACHINE_RESET_MODE_SOFT; +} + +int Controller::attachDriveImage(const char* val) +{ + // Currently only drive 8 is supported + if(!strcmp(val, "Empty")) + return -1; + + if (file_system_attach_disk(8, val) < 0) + return -1; + + return 0; +} + +int Controller::attachTapeImage(const char* val) +{ + if(!strcmp(val, "Empty")) + return -1; + + if (tape_image_attach(1, val) < 0) + return -1; + + return 0; +} + +int Controller::attachCartridgeImage(const char* val) +{ + if(!strcmp(val, "Empty")) + return -1; + + if (cartridge_attach_image(CARTRIDGE_CRT, val) < 0) + return -1; + + return 0; +} + +void Controller::detachDriveImage() +{ + file_system_detach_disk(8); +} + +int Controller::detachTapeImage() +{ + return tape_image_detach(1); +} + +void Controller::detachCartridgeImage() +{ + cartridge_detach_image(-1); + + // Detaching cartridge will result in a hard reset if 'CartridgeReset' is set. + int cartridge_reset; + resources_get_int(VICE_RES_CARTRIDGE_RESET, &cartridge_reset); + if (cartridge_reset) + gs_view->notifyReset(); +} + +void Controller::syncSetting(int key) +{ + // Sync setting with VICE. + + string value; + const char* image_file = NULL; + + switch (key){ + case DRIVE8: + // Get drive image and it's contents. + image_file = file_system_get_disk_name(8); + if (!image_file){ + // Empty peripheral + gs_view->onSettingChanged(key,"Empty","",0,0,3); + } + else{ + image_contents_t* content = NULL; + const char* value; + const char* value2; + const char** list; + int size = 0; + + gs_view->getSettingValues(key, &value, &value2, &list, &size); + + if (!strcmp(value, "Empty")){ + // Image is attached to device but is not showing in settings. + content = getImageContent(key, image_file); + getImageFileContents(key, image_file, &list, &size, content); + if (size){ + gs_view->onSettingChanged(key, list[0], image_file, list, size, 15); + } + } + else{ + // Device has attachement and something is showing in settings. + // Do nothing if image is current. + if (!strcmp(value2, image_file)){ + return; + } + // New image. First delete and deallocate old values. + if (list && size){ + const char** p = list; + for (int i=0; ionSettingChanged(key, list[0], image_file, list, size, 15); + } + } + + if (content){ + image_contents_destroy(content); + } + } + break; + case DRIVE_TRUE_EMULATION: + { + int val; + string str_val; + + if (resources_get_int(VICE_RES_DRIVE_TRUE_EMULATION, &val) < 0) + return; + + str_val = val? "True": "Fast"; + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + break; + } + case DRIVE_SOUND_EMULATION: + { + int val; + string str_val; + + if (resources_get_int(VICE_RES_DRIVE_SOUND_EMULATION, &val) < 0) + return; + + str_val = val? "Enabled": "Disabled"; + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + break; + } + case DATASETTE: + { + const char* curr_image_file; + const char** curr_values; + int list_size; + + gs_view->getSettingValues(key, 0, &curr_image_file, &curr_values, &list_size); + image_file = tape_get_file_name(); + + if (!image_file){ + // No tape image attached. Do nothing. + return; + } + + if (!strcmp(curr_image_file, image_file)){ + // Same tape image attached. Do nothing. + return; + } + + // New tape image attached. Delete old list. + if (list_size > 0){ + const char** p = curr_values; + for (int i=0; ionSettingChanged(key, value.c_str(), image_file,0,0,15); + break; + } + case DATASETTE_RESET_WITH_CPU: + { + int val; + string str_val; + + if (resources_get_int(VICE_RES_DATASETTE_RESET_WITH_CPU, &val) < 0) + return; + + str_val = val? "Enabled": "Disabled"; + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + break; + } + case CARTRIDGE: + image_file = cartridge_get_file_name(cart_getid_slotmain()); + if (image_file) + gs_view->onSettingChanged(key, getFileNameFromPath(image_file).c_str(),image_file,0,0,3); + else + gs_view->onSettingChanged(key, "Empty","",0,0,3); + break; + case CARTRIDGE_RESET: + { + int val; + string str_val; + + if (resources_get_int(VICE_RES_CARTRIDGE_RESET, &val) < 0) + return; + + str_val = val? "Enabled": "Disabled"; + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + break; + } + case VICII_MODEL: + { + int val; + string str_val; + + if (resources_get_int(VICE_RES_MACHINE_VIDEO_STANDARD, &val) < 0) + return; + + switch (val){ + case MACHINE_SYNC_PAL: + str_val = "PAL"; + break; + case MACHINE_SYNC_NTSC: + str_val = "NTSC"; + break; + case MACHINE_SYNC_NTSCOLD: + str_val = "Old NTSC"; + break; + case MACHINE_SYNC_PALN: + str_val = "PAL-N"; + break; + } + + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + break; + } + case SID_ENGINE: + { + int val; + string str_val; + + if (resources_get_int(VICE_RES_SID_ENGINE, &val) < 0) + return; + + switch (val){ + case SID_ENGINE_FASTSID: str_val = "FastSID"; break; + case SID_ENGINE_RESID: str_val = "ReSID"; break; + } + + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + break; + } + case SID_MODEL: + { + int val; + string str_val; + + if (resources_get_int(VICE_RES_SID_MODEL, &val) < 0) + return; + + switch (val){ + case SID_MODEL_6581: str_val = "6581"; break; + case SID_MODEL_8580: str_val = "8580"; break; + } + + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + break; + } + case JOYSTICK_PORT: + { + int val_port1; + int val_port2; + string str_val; + + // Check what we have attached on the control ports. + // 0=none, 1=joystick, 2=paddles, 3=mouse(1351) etc. + if (resources_get_int(VICE_RES_JOY_PORT1_DEV, &val_port1) < 0) + return; + + if (resources_get_int(VICE_RES_JOY_PORT2_DEV, &val_port2) < 0) + return; + + if (val_port1 == 1 && val_port2 != 1){ + g_joystickPort = 1; + str_val = "Port 1"; + } + else if (val_port2 == 1 && val_port1 != 1){ + g_joystickPort = 2; + str_val = "Port 2"; + } + + if (!str_val.empty()) + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + + break; + } + case CPU_SPEED: + { + int val; + string str_val; + + if (resources_get_int(VICE_RES_CPU_SPEED, &val) < 0) + return; + + switch (val){ + case 100: str_val = "100%"; break; + case 125: str_val = "125%"; break; + case 150: str_val = "150%"; break; + case 175: str_val = "175%"; break; + case 200: str_val = "200%"; break; + } + + if (!str_val.empty()) + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + + break; + } + case SOUND: + { + int val; + string str_val; + if (resources_get_int(VICE_RES_SOUND, &val) < 0) + return; + + str_val = val==1? "Enabled": "Disabled"; + gs_view->onSettingChanged(key, str_val.c_str(),0,0,0,1); + break; + } + default: + break; + } +} + +void Controller::syncPeripherals() +{ + syncSetting(DRIVE8); + syncSetting(DRIVE_TRUE_EMULATION); + syncSetting(DRIVE_SOUND_EMULATION); + syncSetting(DATASETTE); + syncSetting(CARTRIDGE); + syncSetting(CARTRIDGE_RESET); +} + +void Controller::syncModelSettings() +{ + syncSetting(VICII_MODEL); + syncSetting(DRIVE_SOUND_EMULATION); + syncSetting(SID_ENGINE); + syncSetting(SID_MODEL); + syncSetting(COLOR_PALETTE); + syncSetting(JOYSTICK_PORT); + syncSetting(CPU_SPEED); + syncSetting(SOUND); + syncSetting(DRIVE_TRUE_EMULATION); + syncSetting(DRIVE_SOUND_EMULATION); + syncSetting(CARTRIDGE_RESET); +} + +void Controller::updatePalette() +{ + video_psv_update_palette(); +} + +void Controller::resumeSound() +{ + sound_resume(); +} + +string Controller::getFileNameFromPath(const char* fpath) +{ + string fname = fpath; + + size_t slash_pos = fname.find_last_of("/"); + if (slash_pos != string::npos){ + return fname.substr(slash_pos+1, string::npos); + } + + size_t colon_pos = fname.find_last_of(":"); + if (colon_pos != string::npos){ + return fname.substr(colon_pos+1, string::npos); + } + + return fname; +} + +int Controller::getImageType(const char* image) +{ + int ret = 0; + string extension; + string file = image; + + static const char* disk_ext[] = {"D64","D71","D80","D81","D82","G64","G41","X64",0}; + static const char* tape_ext[] = {"T64","TAP",0}; + static const char* cart_ext[] = {"CRT",0}; + static const char* prog_ext[] = {"PRG","P00",0}; + + size_t dot_pos = file.find_last_of("."); + if (dot_pos != string::npos) + extension = file.substr(dot_pos+1, string::npos); + + if (extension.empty()) + return ret; + + strToUpperCase(extension); + + const char** p = disk_ext; + + while (*p){ + if (!strcmp(extension.c_str(), *p)) + return IMAGE_DISK; + p++; + } + + p = tape_ext; + + while (*p){ + if (!strcmp(extension.c_str(), *p)) + return IMAGE_TAPE; + p++; + } + + p = cart_ext; + + while (*p){ + if (!strcmp(extension.c_str(), *p)) + return IMAGE_CARTRIDGE; + p++; + } + + p = prog_ext; + + while (*p){ + if (!strcmp(extension.c_str(), *p)) + return IMAGE_PROGRAM; + p++; + } + + return ret; +} + +void Controller::setTapeControl(int action) +{ + switch (action){ + case DATASETTE_CONTROL_STOP: + case DATASETTE_CONTROL_START: + case DATASETTE_CONTROL_FORWARD: + case DATASETTE_CONTROL_REWIND: + case DATASETTE_CONTROL_RECORD: + case DATASETTE_CONTROL_RESET: + case DATASETTE_CONTROL_RESET_COUNTER: + datasette_control(action); + break; + } +} + +void Controller::setCartControl(int action) +{ + switch (action){ + case CART_CONTROL_FREEZE: + keyboard_clear_keymatrix(); + cartridge_trigger_freeze(); + break; + case CART_CONTROL_SET_DEFAULT: + cartridge_set_default(); + break; + case CART_CONTROL_FLUSH_IMAGE: + break; + case CART_CONTROL_SAVE_IMAGE: + break; + } +} + +void Controller::strToUpperCase(string& str) +{ + for (std::string::iterator p = str.begin(); p != str.end(); ++p) + *p = toupper(*p); +} + +void Controller::getViewport(ViewPort* vp, bool borders) +{ + struct video_canvas_s* canvas; + + video_psv_get_canvas(&canvas); + + if (borders){ + vp->width = canvas->draw_buffer->canvas_width; + vp->height = canvas->draw_buffer->canvas_height; + vp->x = canvas->geometry->extra_offscreen_border_left; + vp->y = canvas->geometry->first_displayed_line; + } + else{ + vp->width = canvas->geometry->gfx_size.width; + vp->height = canvas->geometry->gfx_size.height; + vp->x = canvas->geometry->extra_offscreen_border_left + canvas->geometry->gfx_position.x; + vp->y = canvas->geometry->gfx_position.y; + } +} + +int Controller::attachImage(int device, const char* image, const char** curr_values, int curr_val_size) +{ + // Check if device is correct. + int type = getImageType(image); + switch (type){ + case IMAGE_DISK: + case IMAGE_PROGRAM: + if (device != DRIVE8) return -1; + break; + case IMAGE_TAPE: + if (device != DATASETTE) return -1; + break; + case IMAGE_CARTRIDGE: + if (device != CARTRIDGE) return -1; + } + + // Detach old image and deallocate list values + if (curr_values && curr_val_size > 0) + detachImage(device, curr_values, curr_val_size); + + image_contents_t* content = NULL; + + + switch (device){ + case DRIVE8: + if (attachDriveImage(image) < 0) + return -1; + content = diskcontents_read(image, 8); + break; + case DATASETTE: + if (attachTapeImage(image) < 0) + return -1; + content = tapecontents_read(image); + break; + case CARTRIDGE: + if (attachCartridgeImage(image) < 0) + return -1; + break; + } + + const char** list_values = 0; + int list_size = 0; + + if(content){ + // Retrieve content list. + image_contents_file_list_t* entry = content->file_list; + + // Get list size; + while (entry){ + list_size++; + entry = entry->next; + } + + if (list_size){ + list_values = new const char*[list_size]; + const char** p = list_values; + entry = content->file_list; + for (int i=0; inext; + p++; + } + } + } + + string value = list_size? list_values[0]: getFileNameFromPath(image).c_str(); + gs_view->onSettingChanged(device, value.c_str(), image, list_values, list_size, 15); + + if (content) + image_contents_destroy(content); + + return 0; +} + +void Controller::detachImage(int peripheral, const char** values, int size) +{ + switch (peripheral){ + case DRIVE8: + detachDriveImage(); + break; + case DATASETTE: + detachTapeImage(); + break; + case CARTRIDGE: + detachCartridgeImage(); + } + + // Deallocate all the values. + if (values && size){ + const char** p = values; + for (int i=0; ionSettingChanged(peripheral,"Empty","",0,0,15); +} + +image_contents_t* Controller::getImageContent(int peripheral, const char* image) +{ + image_contents_t* ret = NULL; + + if (peripheral == DRIVE8){ + ret = diskcontents_read(image, 8); + } + else if (peripheral == DATASETTE){ + ret = tapecontents_read(image); + } + + return ret; +} + +static void toggleJoystickPorts() +{ + if (g_joystickPort == 1){ + g_joystickPort = 2; + resources_set_int(VICE_RES_JOY_PORT1_DEV, 0); // 0 = None + resources_set_int(VICE_RES_JOY_PORT2_DEV, 1); // 1 = Joystick + gs_view->onSettingChanged(JOYSTICK_PORT,"Port 2","",0,0,1); + } + else{ + g_joystickPort = 1; + resources_set_int(VICE_RES_JOY_PORT1_DEV, 1); + resources_set_int(VICE_RES_JOY_PORT2_DEV, 0); + gs_view->onSettingChanged(JOYSTICK_PORT,"Port 1","",0,0,1); + } +} + +static void toggleWarpMode() +{ + int value; + if (resources_get_int(VICE_RES_WARP_MODE, &value) < 0) + return; + + value = value? 0:1; + resources_set_int(VICE_RES_WARP_MODE, value); +} + +static void checkPendingActions() +{ + // Check for pending actions. This function is called at the end of each screen frame. + // That's every 20ms in PAL standard and every 16ms in NTSC standard. + + if (gs_showMenuTimer > 0){ + if (--gs_showMenuTimer == 0) + video_psv_menu_show(); + } + if (gs_pauseTimer > 0){ + if (--gs_pauseTimer == 0){ + ui_pause_emulation(1); + gs_view->displayPaused(1); + gs_view->updateView(); + } + } + if (gs_loadDiskTimer > 0){ + if (--gs_loadDiskTimer == 0){ + char* cmd = lib_msprintf("LOAD\"%s\",8,1:\r", gs_loadProgramName.c_str()); + kbdbuf_feed(cmd); + lib_free(cmd); + // Schedule run command. + setPendingAction(CTRL_ACTION_KBDCMD_RUN); + } + } + if (gs_loadTapeTimer > 0){ + if (--gs_loadTapeTimer == 0){ + kbdbuf_feed("LOAD\r"); + datasette_control(DATASETTE_CONTROL_START); + setPendingAction(CTRL_ACTION_KBDCMD_RUN); + } + } + if (gs_kbdCmdRunTimer > 0){ + if (--gs_kbdCmdRunTimer == 0){ + kbdbuf_feed("RUN\r"); + } + } +} + +static void setPendingAction(CTRL_ACTION action) +{ + // Set actions that for some reason need to be performed after a delay. + + // Show menu and pause: + // Drain the audible sound buffer before the action. + // This is done to prevent hearing remains of previous game audio when loading a new game. + // The trick here is to just put the volume down, wait about 200ms until there is + // silence in the buffer and then performing the action. + // TODO: Find a better way to empty the sound buffer. + // + // Load tape/disk: + // When loading files from peripherals we need to delay the load for the property changes + // to be in effect. For instance, if you change drive sound to enabled just before you load, + // you won't hear anything. Same thing when you change drive mode to true, the load will get stuck. + // + // Run commmand: + // The Run command must be fed to the keyboard queue after the loading commands + // have been printed to the screen. The command will stay in the queue until the ready + // message appears with the blinking cursor. + + switch (action){ + + case CTRL_ACTION_SHOW_MENU: + if (!gs_showMenuTimer){ + gs_showMenuTimer = 10; + setSoundVolume(0); + } + break; + case CTRL_ACTION_PAUSE: + if (!gs_pauseTimer){ + gs_pauseTimer = 10; + setSoundVolume(0); + } + break; + case CTRL_ACTION_LOAD_DISK: + if (!gs_loadDiskTimer){ + gs_loadDiskTimer = 50; + } + break; + case CTRL_ACTION_LOAD_TAPE: + if (!gs_loadTapeTimer){ + gs_loadTapeTimer = 10; + } + break; + case CTRL_ACTION_KBDCMD_RUN: + if (!gs_kbdCmdRunTimer){ + gs_kbdCmdRunTimer = 5; + } + break; + } +} + +static void setSoundVolume(int vol) +{ + resources_set_int(VICE_RES_SOUND_VOLUME, vol); +} + diff --git a/src/arch/psvita/controller/controller.h b/src/arch/psvita/controller/controller.h new file mode 100644 index 0000000..9038777 --- /dev/null +++ b/src/arch/psvita/controller/controller.h @@ -0,0 +1,149 @@ + +/* controller.cpp: Purpose of the controller is to act as a middle man + between View and Model (VICE). + + Copyright (C) 2019-2020 Amnon-Dan Meir. + + 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 . + + Author contact information: + Email: ammeir71@yahoo.com +*/ + +#ifndef CONTROLLER_H +#define CONTROLLER_H + +#ifndef __cplusplus +// for gcc +void PSV_CreateView(int width, int height, int depth); +void PSV_UpdateView(); +void PSV_SetViewport(int x, int y, int width, int height); +void PSV_GetViewInfo(int* width, int* height, unsigned char** ppixels, int* pitch, int* bpp); +void PSV_ScanControls(); +void PSV_ApplySettings(); +void PSV_ActivateMenu(); +int PSV_RGBToPixel(uint8_t r, uint8_t g, uint8_t b); +void PSV_NotifyPalette(unsigned char* palette, int size); +void PSV_NotifyFPS(int fps, float percent, int warp_flag); +void PSV_NotifyTapeCounter(int count); +void PSV_NotifyTapeControl(int control); +void PSV_NotifyReset(); +int PSV_ShowMessage(const char* msg, int msg_type); +#else + +#include "view.h" +#include +#include + +extern "C" { +#include "imagecontents.h" +} + +extern int g_joystickPort; + +typedef struct{ + string fname; + string fpath; +}file_info_s; + +typedef struct{ + int row; + int column; + int ispress; +}key_action_s; + +typedef struct{ + const char* snapshot_file; + const char* module_name; + uint8_t major; + uint8_t minor; + const char* data; + uint32_t data_size; +}patch_data_s; + +typedef enum{ + AUTO_DETECT_LOAD, + DISK_LOAD, + TAPE_LOAD, + CART_LOAD +}load_type_e; + +using std::string; +using std::vector; + +class View; +class Controller +{ + +private: + bool m_inGame; + + string getFileNameFromPath(const char* fpath); + void changeJoystickPort(const char* port); + void setCpuSpeed(const char* val); + void setAudioPlayback(const char* val); + void setSidEngine(const char* val); + void setSidModel(const char* val); + void setViciiModel(const char* val); + void setCrtEmulation(); + void setColorPalette(const char* val); + void setJoystickPort(const char* val); + void setDriveEmulation(const char* val); + void setDriveSoundEmulation(const char* val); + void setDatasetteReset(const char* val); + void setCartridgeReset(const char* val); + void setMachineResetMode(const char* val); + void setMouseSampling(const char* val); + void updatePalette(); + void resumeSound(); + int attachDriveImage(const char* image); + int attachTapeImage(const char* image); + int attachCartridgeImage(const char* image); + void detachDriveImage(); + int detachTapeImage(); + void detachCartridgeImage(); + string getFileExtension(const char* fname); + void strToUpperCase(string& str); + image_contents_t* getImageContent(int peripheral, const char* image); + + +public: + Controller(); + ~Controller(); + + void init(View* view); + void resetComputer(); + int loadFile(load_type_e type, const char* file_path, int index = 0, const char* target_file = NULL); + int loadState(const char* file); + int saveState(const char* file_name); + int patchSaveState(patch_data_s* patch); + int getSaveStatePatch(patch_data_s* patch_info); + int getSaveStatePatchInfo(patch_data_s* patch_info); + void getViewport(ViewPort* vp, bool borders); + void syncSetting(int key); + void syncPeripherals(); + void syncModelSettings(); + void setModelProperty(int key, const char* value); + void getImageFileContents(int peripheral, const char* image, const char*** values, int* size, image_contents_t* content); + int attachImage(int device, const char* image, const char** values, int size); + void detachImage(int device, const char** values, int size); + int getImageType(const char* image); + void setTapeControl(int action); + void setCartControl(int action); + void setBorderVisibility(const char* val); +}; + + +#endif +#endif \ No newline at end of file diff --git a/src/arch/psvita/controller/ctrl_defs.h b/src/arch/psvita/controller/ctrl_defs.h new file mode 100644 index 0000000..aef7b3a --- /dev/null +++ b/src/arch/psvita/controller/ctrl_defs.h @@ -0,0 +1,32 @@ + +#ifndef CTRL_DEFS_H +#define CTRL_DEFS_H + +enum CTRL_ACTION { + CTRL_ACTION_SHOW_MENU = 0, + CTRL_ACTION_PAUSE, + CTRL_ACTION_LOAD_DISK, + CTRL_ACTION_LOAD_TAPE, + CTRL_ACTION_KBDCMD_RUN +}; + +static bool gs_frameDrawn = false; +static bool gs_bootTime = true; +static int gs_showMenuTimer = 0; +static int gs_pauseTimer = 0; +static int gs_loadDiskTimer = 0; +static int gs_loadTapeTimer = 0; +static int gs_kbdCmdRunTimer = 0; +static bool gs_scanMouse = false; +static int gs_machineResetMode = 1; +static string gs_loadProgramName; +int g_joystickPort = 2; + +static void toggleJoystickPorts(); +static void toggleWarpMode(); +static void setPendingAction(CTRL_ACTION); +static void checkPendingActions(); +static void setSoundVolume(int); + +#endif +