1825 lines
73 KiB
C++
1825 lines
73 KiB
C++
#include <stdio.h>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <sys/stat.h>
|
|
|
|
|
|
#if defined(_WIN32)
|
|
#include "dirent/dirent.h"
|
|
#undef max
|
|
#undef min
|
|
#else
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
#include <SDL.h>
|
|
#include "memory.h"
|
|
#include "cpu.h"
|
|
#include "audio.h"
|
|
#include "editor.h"
|
|
#include "loader.h"
|
|
#include "timing.h"
|
|
#include "image.h"
|
|
#include "graphics.h"
|
|
#include "terminal.h"
|
|
#include "expression.h"
|
|
#include "assembler.h"
|
|
#include "keywords.h"
|
|
#include "inih/INIReader.h"
|
|
|
|
|
|
namespace Editor
|
|
{
|
|
enum SingleStepMode {RunToBrk=0, StepPC, StepWatch, NumSingleStepModes};
|
|
|
|
struct FileEntry
|
|
{
|
|
FileType _fileType;
|
|
std::string _name;
|
|
};
|
|
|
|
|
|
int _cursorX = 0;
|
|
int _cursorY = 0;
|
|
|
|
bool _hexEdit = false;
|
|
|
|
SDL_Keycode _sdlKeyScanCode = 0;
|
|
uint16_t _sdlKeyModifier = 0;
|
|
|
|
bool _pageUpButton = false;
|
|
bool _pageDnButton = false;
|
|
bool _delAllButton = false;
|
|
|
|
bool _singleStep = false;
|
|
bool _singleStepEnabled = false;
|
|
SingleStepMode _singleStepMode = RunToBrk;
|
|
uint32_t _singleStepTicks = 0;
|
|
uint16_t _singleStepNtv = 0x0000;
|
|
uint16_t _singleStepVpc = 0x0000;
|
|
uint16_t _singleStepAddress = FRAME_COUNT_ADDRESS;
|
|
|
|
std::string _browserPath = "./";
|
|
|
|
MouseState _mouseState;
|
|
MemoryMode _memoryMode = RAM;
|
|
EditorMode _editorMode = Hex;
|
|
EditorMode _editorModePrev = Hex;
|
|
KeyboardMode _keyboardMode = Giga;
|
|
OnVarType _onVarType = OnNone;
|
|
|
|
uint8_t _memoryDigit = 0;
|
|
uint8_t _addressDigit = 0;
|
|
|
|
uint16_t _ntvBaseAddress = 0x0000;
|
|
uint16_t _hexBaseAddressROM = 0x0000;
|
|
uint16_t _hexBaseAddressRAM = HEX_BASE_ADDRESS;
|
|
uint16_t _vpcBaseAddress = HEX_BASE_ADDRESS;
|
|
uint16_t _loadBaseAddress = LOAD_BASE_ADDRESS;
|
|
uint16_t _varsBaseAddress = VARS_BASE_ADDRESS;
|
|
|
|
uint16_t _cpuUsageAddressA = HEX_BASE_ADDRESS;
|
|
uint16_t _cpuUsageAddressB = HEX_BASE_ADDRESS + 0x0020;
|
|
|
|
std::vector<uint16_t> _ntvBreakPoints;
|
|
std::vector<uint16_t> _vpcBreakPoints;
|
|
|
|
int _fileEntriesSize = 0;
|
|
int _fileEntriesIndex = 0;
|
|
std::vector<FileEntry> _fileEntries;
|
|
|
|
int _romEntriesSize = 0;
|
|
int _romEntriesIndex = 0;
|
|
std::vector<RomEntry> _romEntries;
|
|
|
|
INIReader _configIniReader;
|
|
std::map<std::string, int> _sdlKeys;
|
|
std::map<std::string, KeyCodeMod> _emulator;
|
|
std::map<std::string, KeyCodeMod> _keyboard;
|
|
std::map<std::string, KeyCodeMod> _hardware;
|
|
std::map<std::string, KeyCodeMod> _debugger;
|
|
|
|
|
|
int getCursorX(void) {return _cursorX;}
|
|
int getCursorY(void) {return _cursorY;}
|
|
bool getHexEdit(void) {return _hexEdit;}
|
|
bool getSingleStepEnabled(void) {return _singleStepEnabled;}
|
|
|
|
bool getPageUpButton(void) {return _pageUpButton;}
|
|
bool getPageDnButton(void) {return _pageDnButton;}
|
|
bool getDelAllButton(void) {return _delAllButton;}
|
|
|
|
MemoryMode getMemoryMode(void) {return _memoryMode;}
|
|
EditorMode getEditorMode(void) {return _editorMode;}
|
|
EditorMode getEditorModePrev(void) {return _editorModePrev;}
|
|
KeyboardMode getKeyboardMode(void) {return _keyboardMode;}
|
|
OnVarType getOnVarType(void) {return _onVarType;}
|
|
|
|
uint8_t getMemoryDigit(void) {return _memoryDigit;}
|
|
uint8_t getAddressDigit(void) {return _addressDigit;}
|
|
uint16_t getNtvBaseAddress(void) {return _ntvBaseAddress;}
|
|
uint16_t getVpcBaseAddress(void) {return _vpcBaseAddress;}
|
|
uint16_t getLoadBaseAddress(void) {return _loadBaseAddress;}
|
|
uint16_t getVarsBaseAddress(void) {return _varsBaseAddress;}
|
|
uint16_t getSingleStepAddress(void) {return _singleStepAddress;}
|
|
uint16_t getCpuUsageAddressA(void) {return _cpuUsageAddressA;}
|
|
uint16_t getCpuUsageAddressB(void) {return _cpuUsageAddressB;}
|
|
|
|
int getNtvBreakPointsSize(void) {return int(_ntvBreakPoints.size());}
|
|
uint16_t getNtvBreakPointAddress(int index) {return _ntvBreakPoints.size() ? _ntvBreakPoints[index % _ntvBreakPoints.size()] : 0;}
|
|
void addNtvBreakPoint(uint16_t address) {_ntvBreakPoints.push_back(address);}
|
|
void clearNtvBreakPoints(void) {_ntvBreakPoints.clear();}
|
|
|
|
int getVpcBreakPointsSize(void) {return int(_vpcBreakPoints.size());}
|
|
uint16_t getVpcBreakPointAddress(int index) {return _vpcBreakPoints.size() ? _vpcBreakPoints[index % _vpcBreakPoints.size()] : 0;}
|
|
void addVpcBreakPoint(uint16_t address) {_vpcBreakPoints.push_back(address);}
|
|
void clearVpcBreakPoints(void) {_vpcBreakPoints.clear();}
|
|
|
|
int getFileEntriesIndex(void) {return _fileEntriesIndex;}
|
|
int getFileEntriesSize(void) {return int(_fileEntries.size());}
|
|
FileType getFileEntryType(int index) {return _fileEntries.size() ? _fileEntries[index % _fileEntries.size()]._fileType : File;}
|
|
FileType getCurrentFileEntryType(void) {return _fileEntries.size() ? _fileEntries[(_cursorY + _fileEntriesIndex) % _fileEntries.size()]._fileType : File;}
|
|
std::string* getFileEntryName(int index) {return _fileEntries.size() ? &_fileEntries[index % _fileEntries.size()]._name : nullptr;}
|
|
std::string* getCurrentFileEntryName(void) {return _fileEntries.size() ? &_fileEntries[(_cursorY + _fileEntriesIndex) % _fileEntries.size()]._name : nullptr;}
|
|
|
|
int getRomEntriesIndex(void) {return _romEntriesIndex;}
|
|
int getRomEntriesSize(void) {return int(_romEntries.size());}
|
|
uint8_t getRomEntryType(int index) {return _romEntries.size() ? _romEntries[index % _romEntries.size()]._type : 0;}
|
|
uint8_t getCurrentRomEntryType(int& index) {if(_romEntries.size() == 0) return 0; index = (_cursorY + _romEntriesIndex) % _romEntries.size(); return _romEntries[index]._type;}
|
|
std::string* getRomEntryName(int index) {return _romEntries.size() ? &_romEntries[index % _romEntries.size()]._name : nullptr;}
|
|
std::string* getCurrentRomEntryName(int& index) {if(_romEntries.size() == 0) return nullptr; index = (_cursorY + _romEntriesIndex) % _romEntries.size(); return &_romEntries[index]._name;}
|
|
int getCurrentRomEntryIndex(void) {return _romEntries.size() ? (_cursorY + _romEntriesIndex) % _romEntries.size() : 0;}
|
|
void addRomEntry(uint8_t type, std::string& name) {Editor::RomEntry romEntry = {type, name}; _romEntries.push_back(romEntry); return;}
|
|
|
|
void resetEditor(void) {_memoryDigit = 0; _addressDigit = 0;}
|
|
void setEditorToPrevMode(void) {_editorMode = _editorModePrev; if(_editorMode == Load) browseDirectory();}
|
|
void setEditorMode(EditorMode editorMode) {_editorModePrev = _editorMode; _editorMode = editorMode;}
|
|
|
|
void setCursorX(int x) {_cursorX = x;}
|
|
void setCursorY(int y) {_cursorY = y;}
|
|
void setHexEdit(bool hexEdit) {_hexEdit = hexEdit; if(!hexEdit) resetEditor();}
|
|
void setSingleStepAddress(uint16_t address) {_singleStepAddress = address;}
|
|
void setLoadBaseAddress(uint16_t address) {_loadBaseAddress = address;}
|
|
void setCpuUsageAddressA(uint16_t address) {_cpuUsageAddressA = address;}
|
|
void setCpuUsageAddressB(uint16_t address) {_cpuUsageAddressB = address;}
|
|
|
|
void getMouseState(MouseState& mouseState) {mouseState = _mouseState;}
|
|
void setMouseState(MouseState& mouseState) {_mouseState = mouseState;}
|
|
|
|
uint16_t getHexBaseAddress(void)
|
|
{
|
|
switch(Editor::getMemoryMode())
|
|
{
|
|
case Editor::RAM: return _hexBaseAddressRAM; break;
|
|
case Editor::ROM0: return _hexBaseAddressROM; break;
|
|
case Editor::ROM1: return _hexBaseAddressROM; break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
return 0x0000;
|
|
}
|
|
|
|
void setHexBaseAddress(uint16_t hexBaseAddress)
|
|
{
|
|
switch(Editor::getMemoryMode())
|
|
{
|
|
case Editor::RAM: _hexBaseAddressRAM = hexBaseAddress; break;
|
|
case Editor::ROM0: _hexBaseAddressROM = hexBaseAddress; break;
|
|
case Editor::ROM1: _hexBaseAddressROM = hexBaseAddress; break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void getMouseUiCursor(int& x, int& y, int& cy)
|
|
{
|
|
// Normalised mouse position
|
|
float mx = float(_mouseState._x) / float(Graphics::getWidth());
|
|
float my = float(_mouseState._y) / float(Graphics::getHeight());
|
|
|
|
// PageUp, PageDn and DelAll buttons
|
|
float pux0 = float(UI_START_X+PAGEUP_START_X) / float(SCREEN_WIDTH);
|
|
float puy0 = float(UI_START_Y+PAGEUP_START_Y) / float(SCREEN_HEIGHT);
|
|
float pux1 = float(UI_START_X+PAGEUP_START_X+FONT_WIDTH) / float(SCREEN_WIDTH);
|
|
float puy1 = float(UI_START_Y+PAGEUP_START_Y+FONT_HEIGHT) / float(SCREEN_HEIGHT);
|
|
float pdx0 = float(UI_START_X+PAGEDN_START_X) / float(SCREEN_WIDTH);
|
|
float pdy0 = float(UI_START_Y+PAGEDN_START_Y) / float(SCREEN_HEIGHT);
|
|
float pdx1 = float(UI_START_X+PAGEDN_START_X+FONT_WIDTH) / float(SCREEN_WIDTH);
|
|
float pdy1 = float(UI_START_Y+PAGEDN_START_Y+FONT_HEIGHT) / float(SCREEN_HEIGHT);
|
|
float dax0 = float(UI_START_X+DELALL_START_X) / float(SCREEN_WIDTH);
|
|
float day0 = float(UI_START_Y+DELALL_START_Y) / float(SCREEN_HEIGHT);
|
|
float dax1 = float(UI_START_X+DELALL_START_X+FONT_WIDTH) / float(SCREEN_WIDTH);
|
|
float day1 = float(UI_START_Y+DELALL_START_Y+FONT_HEIGHT) / float(SCREEN_HEIGHT);
|
|
|
|
_pageUpButton = (mx >= pux0 && mx < pux1 && my >= puy0 && my < puy1);
|
|
_pageDnButton = (mx >= pdx0 && mx < pdx1 && my >= pdy0 && my < pdy1);
|
|
_delAllButton = (mx >= dax0 && mx < dax1 && my >= day0 && my < day1);
|
|
|
|
// Normalised cursor origin
|
|
const float ox = float(UI_START_X) / float(SCREEN_WIDTH);
|
|
const float oy = float(UI_START_Y) / float(SCREEN_HEIGHT);
|
|
|
|
// Hex editing can only happen in the hex window
|
|
if(mx < ox) _hexEdit = false;
|
|
|
|
// Ui text cursor positions
|
|
x = -1, y = -1, cy = -5;
|
|
if(mx >= ox && mx < 0.98f && my >= oy && my < 1.0f)
|
|
{
|
|
x = int((mx - ox) * 1.0f / (1.0f - ox) * (UI_CHARS_X+0.65f));
|
|
y = int((my - oy) * 1.0f / (1.0f - oy) * UI_CHARS_Y);
|
|
cy = y - 4;
|
|
}
|
|
|
|
//fprintf(stderr, "%d %d %d %d %d %d %08x\n", x, y, cy, _pageUpButton, _pageDnButton, _delAllButton, _mouseState._state);
|
|
}
|
|
|
|
bool scanCodeFromIniKey(const std::string& sectionString, const std::string& iniKey, const std::string& defaultKey, KeyCodeMod& keyCodeMod)
|
|
{
|
|
keyCodeMod._keyMod = KMOD_NONE;
|
|
|
|
std::string mod;
|
|
std::string key = _configIniReader.Get(sectionString, iniKey, defaultKey);
|
|
Expression::strToUpper(key);
|
|
|
|
// Parse CTRL or ALT
|
|
size_t keyPos = key.find("+");
|
|
if(keyPos != std::string::npos && keyPos != key.length()-1)
|
|
{
|
|
mod = key.substr(0, keyPos);
|
|
key = key.substr(keyPos + 1);
|
|
if(mod == "ALT") keyCodeMod._keyMod = KMOD_LALT;
|
|
if(mod == "CTRL") keyCodeMod._keyMod = KMOD_LCTRL;
|
|
}
|
|
|
|
if(_sdlKeys.find(key) == _sdlKeys.end())
|
|
{
|
|
fprintf(stderr, "Editor::scanCodeFromIniKey() : key %s not recognised in INI file '%s' : reverting to default key '%s'\n", key.c_str(), INPUT_CONFIG_INI, defaultKey.c_str());
|
|
keyCodeMod._scanCode = _sdlKeys[defaultKey];
|
|
return false;
|
|
}
|
|
|
|
keyCodeMod._scanCode = _sdlKeys[key];
|
|
//fprintf(stderr, "Editor::scanCodeFromIniKey() : %s : %s : %s : %s : key=%d : mod=%d\n", sectionString.c_str(), iniKey.c_str(), key.c_str(), mod.c_str(), keyCodeMod._scanCode, keyCodeMod._keyMod);
|
|
return true;
|
|
}
|
|
|
|
int getEmulatorScanCode(const std::string& keyWord)
|
|
{
|
|
if(_emulator.find(keyWord) == _emulator.end()) return -1;
|
|
|
|
return _emulator[keyWord]._scanCode;
|
|
}
|
|
|
|
SDL_Keymod getEmulatorKeyMod(const std::string& keyWord)
|
|
{
|
|
if(_emulator.find(keyWord) == _emulator.end()) return KMOD_NONE;
|
|
|
|
return _emulator[keyWord]._keyMod;
|
|
}
|
|
|
|
std::string getBrowserPath(bool removeSlash)
|
|
{
|
|
std::string str = _browserPath;
|
|
if(removeSlash && str.length()) str.erase(str.length()-1);
|
|
return str;
|
|
}
|
|
void setBrowserPath(const std::string& path)
|
|
{
|
|
_browserPath = path;
|
|
}
|
|
|
|
|
|
void initialise(void)
|
|
{
|
|
SDL_StartTextInput();
|
|
|
|
_browserPath = Loader::getCwdPath() + "/";
|
|
|
|
// Keyboard to SDL key mapping
|
|
_sdlKeys["ENTER"] = SDLK_RETURN;
|
|
_sdlKeys["CR"] = SDLK_RETURN;
|
|
_sdlKeys["A"] = SDLK_a;
|
|
_sdlKeys["B"] = SDLK_b;
|
|
_sdlKeys["C"] = SDLK_c;
|
|
_sdlKeys["D"] = SDLK_d;
|
|
_sdlKeys["E"] = SDLK_e;
|
|
_sdlKeys["F"] = SDLK_f;
|
|
_sdlKeys["G"] = SDLK_g;
|
|
_sdlKeys["H"] = SDLK_h;
|
|
_sdlKeys["I"] = SDLK_i;
|
|
_sdlKeys["J"] = SDLK_j;
|
|
_sdlKeys["K"] = SDLK_k;
|
|
_sdlKeys["L"] = SDLK_l;
|
|
_sdlKeys["M"] = SDLK_m;
|
|
_sdlKeys["N"] = SDLK_n;
|
|
_sdlKeys["O"] = SDLK_o;
|
|
_sdlKeys["P"] = SDLK_p;
|
|
_sdlKeys["Q"] = SDLK_q;
|
|
_sdlKeys["R"] = SDLK_r;
|
|
_sdlKeys["S"] = SDLK_s;
|
|
_sdlKeys["T"] = SDLK_t;
|
|
_sdlKeys["U"] = SDLK_u;
|
|
_sdlKeys["V"] = SDLK_v;
|
|
_sdlKeys["W"] = SDLK_w;
|
|
_sdlKeys["X"] = SDLK_x;
|
|
_sdlKeys["Y"] = SDLK_y;
|
|
_sdlKeys["Z"] = SDLK_z;
|
|
_sdlKeys["1"] = SDLK_1;
|
|
_sdlKeys["!"] = SDLK_1;
|
|
_sdlKeys["2"] = SDLK_2;
|
|
_sdlKeys["@"] = SDLK_2;
|
|
_sdlKeys["3"] = SDLK_3;
|
|
_sdlKeys["#"] = SDLK_3;
|
|
_sdlKeys["4"] = SDLK_4;
|
|
_sdlKeys["$"] = SDLK_4;
|
|
_sdlKeys["5"] = SDLK_5;
|
|
_sdlKeys["%"] = SDLK_5;
|
|
_sdlKeys["6"] = SDLK_6;
|
|
_sdlKeys["^"] = SDLK_6;
|
|
_sdlKeys["7"] = SDLK_7;
|
|
_sdlKeys["&"] = SDLK_7;
|
|
_sdlKeys["8"] = SDLK_8;
|
|
_sdlKeys["*"] = SDLK_8;
|
|
_sdlKeys["9"] = SDLK_9;
|
|
_sdlKeys["("] = SDLK_9;
|
|
_sdlKeys["0"] = SDLK_0;
|
|
_sdlKeys[")"] = SDLK_0;
|
|
_sdlKeys["F1"] = SDLK_F1;
|
|
_sdlKeys["F2"] = SDLK_F2;
|
|
_sdlKeys["F3"] = SDLK_F3;
|
|
_sdlKeys["F4"] = SDLK_F4;
|
|
_sdlKeys["F5"] = SDLK_F5;
|
|
_sdlKeys["F6"] = SDLK_F6;
|
|
_sdlKeys["F7"] = SDLK_F7;
|
|
_sdlKeys["F8"] = SDLK_F8;
|
|
_sdlKeys["F9"] = SDLK_F9;
|
|
_sdlKeys["F10"] = SDLK_F10;
|
|
_sdlKeys["F11"] = SDLK_F11;
|
|
_sdlKeys["F12"] = SDLK_F12;
|
|
_sdlKeys["SPACE"] = SDLK_SPACE;
|
|
_sdlKeys["BACKSPACE"] = SDLK_BACKSPACE;
|
|
_sdlKeys["TAB"] = SDLK_TAB;
|
|
_sdlKeys["_"] = SDLK_MINUS;
|
|
_sdlKeys["-"] = SDLK_MINUS;
|
|
_sdlKeys["+"] = SDLK_EQUALS;
|
|
_sdlKeys["="] = SDLK_EQUALS;
|
|
_sdlKeys["`"] = SDLK_BACKQUOTE;
|
|
_sdlKeys["~"] = SDLK_BACKQUOTE;
|
|
_sdlKeys["<"] = SDLK_COMMA;
|
|
_sdlKeys[","] = SDLK_COMMA;
|
|
_sdlKeys[">"] = SDLK_PERIOD;
|
|
_sdlKeys["."] = SDLK_PERIOD;
|
|
_sdlKeys["["] = SDLK_LEFTBRACKET;
|
|
_sdlKeys["{"] = SDLK_LEFTBRACKET;
|
|
_sdlKeys["]"] = SDLK_RIGHTBRACKET;
|
|
_sdlKeys["}"] = SDLK_RIGHTBRACKET;
|
|
_sdlKeys[";"] = SDLK_SEMICOLON;
|
|
_sdlKeys[":"] = SDLK_SEMICOLON;
|
|
_sdlKeys["'"] = SDLK_QUOTE;
|
|
_sdlKeys["\""] = SDLK_QUOTE;
|
|
_sdlKeys["\\"] = SDLK_BACKSLASH;
|
|
_sdlKeys["|"] = SDLK_BACKSLASH;
|
|
_sdlKeys["/"] = SDLK_SLASH;
|
|
_sdlKeys["?"] = SDLK_SLASH;
|
|
_sdlKeys["LEFT"] = SDLK_LEFT;
|
|
_sdlKeys["RIGHT"] = SDLK_RIGHT;
|
|
_sdlKeys["UP"] = SDLK_UP;
|
|
_sdlKeys["DOWN"] = SDLK_DOWN;
|
|
_sdlKeys["PAGEUP"] = SDLK_PAGEUP;
|
|
_sdlKeys["PAGEDOWN"] = SDLK_PAGEDOWN;
|
|
_sdlKeys["CAPSLOCK"] = SDLK_CAPSLOCK;
|
|
_sdlKeys["PRINTSCREEN"] = SDLK_PRINTSCREEN;
|
|
_sdlKeys["SCROLLLOCK"] = SDLK_SCROLLLOCK;
|
|
_sdlKeys["ESC"] = SDLK_ESCAPE;
|
|
_sdlKeys["PAUSE"] = SDLK_PAUSE;
|
|
_sdlKeys["INSERT"] = SDLK_INSERT;
|
|
_sdlKeys["HOME"] = SDLK_HOME;
|
|
_sdlKeys["DELETE"] = SDLK_DELETE;
|
|
_sdlKeys["END"] = SDLK_END;
|
|
_sdlKeys["NUMLOCK"] = SDLK_NUMLOCKCLEAR;
|
|
_sdlKeys["KP_DIVIDE"] = SDLK_KP_DIVIDE;
|
|
_sdlKeys["KP_MULTIPLY"] = SDLK_KP_MULTIPLY;
|
|
_sdlKeys["KP_MINUS"] = SDLK_KP_MINUS;
|
|
_sdlKeys["KP_PLUS"] = SDLK_KP_PLUS;
|
|
_sdlKeys["KP_ENTER"] = SDLK_KP_ENTER;
|
|
_sdlKeys["KP_1"] = SDLK_KP_1;
|
|
_sdlKeys["KP_2"] = SDLK_KP_2;
|
|
_sdlKeys["KP_3"] = SDLK_KP_3;
|
|
_sdlKeys["KP_4"] = SDLK_KP_4;
|
|
_sdlKeys["KP_5"] = SDLK_KP_5;
|
|
_sdlKeys["KP_6"] = SDLK_KP_6;
|
|
_sdlKeys["KP_7"] = SDLK_KP_7;
|
|
_sdlKeys["KP_8"] = SDLK_KP_8;
|
|
_sdlKeys["KP_9"] = SDLK_KP_9;
|
|
_sdlKeys["KP_0"] = SDLK_KP_0;
|
|
_sdlKeys["KP_PERIOD"] = SDLK_KP_PERIOD;
|
|
_sdlKeys["LCTRL"] = SDLK_LCTRL;
|
|
_sdlKeys["LSHIFT"] = SDLK_LSHIFT;
|
|
_sdlKeys["LALT"] = SDLK_LALT;
|
|
_sdlKeys["LGUI"] = SDLK_LGUI;
|
|
_sdlKeys["RCTRL"] = SDLK_RCTRL;
|
|
_sdlKeys["RSHIFT"] = SDLK_RSHIFT;
|
|
_sdlKeys["RALT"] = SDLK_RALT;
|
|
_sdlKeys["RGUI"] = SDLK_RGUI;
|
|
|
|
|
|
// Emulator INI key to SDL key mapping
|
|
_emulator["MemoryMode"] = {SDLK_m, KMOD_LCTRL};
|
|
_emulator["MemorySize"] = {SDLK_z, KMOD_LCTRL};
|
|
_emulator["Browse"] = {SDLK_b, KMOD_LCTRL};
|
|
_emulator["RomType"] = {SDLK_r, KMOD_LCTRL};
|
|
_emulator["HexMonitor"] = {SDLK_x, KMOD_LCTRL};
|
|
_emulator["Disassembler"] = {SDLK_d, KMOD_LCTRL};
|
|
_emulator["Terminal"] = {SDLK_t, KMOD_LCTRL};
|
|
_emulator["ImageEditor"] = {SDLK_i, KMOD_LCTRL};
|
|
_emulator["AudioEditor"] = {SDLK_a, KMOD_LCTRL};
|
|
_emulator["ScanlineMode"] = {SDLK_s, KMOD_LCTRL};
|
|
_emulator["Reset"] = {SDLK_F1, KMOD_LCTRL};
|
|
_emulator["Help"] = {SDLK_h, KMOD_LCTRL};
|
|
_emulator["Quit"] = {SDLK_q, KMOD_LCTRL};
|
|
|
|
// Keyboard INI key to SDL key mapping
|
|
_keyboard["Mode"] = {SDLK_k, KMOD_LCTRL};
|
|
_keyboard["Left"] = {SDLK_a, KMOD_NONE};
|
|
_keyboard["Right"] = {SDLK_d, KMOD_NONE};
|
|
_keyboard["Up"] = {SDLK_w, KMOD_NONE};
|
|
_keyboard["Down"] = {SDLK_s, KMOD_NONE};
|
|
_keyboard["Start"] = {SDLK_SPACE, KMOD_NONE};
|
|
_keyboard["Select"] = {SDLK_z, KMOD_NONE};
|
|
_keyboard["A"] = {SDLK_GREATER, KMOD_NONE};
|
|
_keyboard["B"] = {SDLK_SLASH, KMOD_NONE};
|
|
|
|
// Hardware INI key to SDL key mapping
|
|
_hardware["Reset"] = {SDLK_F2, KMOD_LCTRL};
|
|
|
|
// Debugger INI key to SDL key mapping
|
|
_debugger["Debug"] = {SDLK_F6, KMOD_LCTRL};
|
|
_debugger["RunToBrk"] = {SDLK_F7, KMOD_LCTRL};
|
|
_debugger["StepPC"] = {SDLK_F8, KMOD_LCTRL};
|
|
_debugger["StepWatch"] = {SDLK_F9, KMOD_LCTRL};
|
|
|
|
// Input configuration
|
|
INIReader iniReader(Loader::getExePath() + "/" + INPUT_CONFIG_INI);
|
|
_configIniReader = iniReader;
|
|
if(_configIniReader.ParseError() < 0)
|
|
{
|
|
fprintf(stderr, "Editor::initialise() : couldn't load INI file '%s' : reverting to default keys.\n", INPUT_CONFIG_INI);
|
|
return;
|
|
}
|
|
|
|
// Parse input keys INI file
|
|
enum Section {Emulator, Keyboard, Hardware, Debugger};
|
|
std::map<std::string, Section> section;
|
|
section["Emulator"] = Emulator;
|
|
section["Keyboard"] = Keyboard;
|
|
section["Hardware"] = Hardware;
|
|
section["Debugger"] = Debugger;
|
|
for(auto sectionString : _configIniReader.Sections())
|
|
{
|
|
if(section.find(sectionString) == section.end())
|
|
{
|
|
fprintf(stderr, "Editor::initialise() : INI file '%s' has bad Sections : reverting to default keys.\n", INPUT_CONFIG_INI);
|
|
break;
|
|
}
|
|
|
|
switch(section[sectionString])
|
|
{
|
|
case Emulator:
|
|
{
|
|
scanCodeFromIniKey(sectionString, "MemoryMode", "CTRL+M", _emulator["MemoryMode"]);
|
|
scanCodeFromIniKey(sectionString, "MemorySize", "CTRL+Z", _emulator["MemorySize"]);
|
|
scanCodeFromIniKey(sectionString, "Browse", "CTRL+B", _emulator["Browse"]);
|
|
scanCodeFromIniKey(sectionString, "RomType", "CTRL+R", _emulator["RomType"]);
|
|
scanCodeFromIniKey(sectionString, "HexMonitor", "CTRL+X", _emulator["HexMonitor"]);
|
|
scanCodeFromIniKey(sectionString, "Disassembler", "CTRL+D", _emulator["Disassembler"]);
|
|
scanCodeFromIniKey(sectionString, "Terminal", "CTRL+T", _emulator["Terminal"]);
|
|
scanCodeFromIniKey(sectionString, "ImageEditor", "CTRL+I", _emulator["ImageEditor"]);
|
|
scanCodeFromIniKey(sectionString, "AudioEditor", "CTRL+A", _emulator["AudioEditor"]);
|
|
scanCodeFromIniKey(sectionString, "ScanlineMode", "CTRL+S", _emulator["ScanlineMode"]);
|
|
scanCodeFromIniKey(sectionString, "Reset", "CTRL+F1", _emulator["Reset"]);
|
|
scanCodeFromIniKey(sectionString, "Help", "CTRL+H", _emulator["Help"]);
|
|
scanCodeFromIniKey(sectionString, "Quit", "CTRL+Q", _emulator["Quit"]);
|
|
}
|
|
break;
|
|
|
|
case Keyboard:
|
|
{
|
|
scanCodeFromIniKey(sectionString, "Mode", "CTRL+K", _keyboard["Mode"]);
|
|
scanCodeFromIniKey(sectionString, "Left", "A", _keyboard["Left"]);
|
|
scanCodeFromIniKey(sectionString, "Right", "D", _keyboard["Right"]);
|
|
scanCodeFromIniKey(sectionString, "Up", "W", _keyboard["Up"]);
|
|
scanCodeFromIniKey(sectionString, "Down", "S", _keyboard["Down"]);
|
|
scanCodeFromIniKey(sectionString, "Start", "SPACE", _keyboard["Start"]);
|
|
scanCodeFromIniKey(sectionString, "Select", "Z", _keyboard["Select"]);
|
|
scanCodeFromIniKey(sectionString, "A", ".", _keyboard["A"]);
|
|
scanCodeFromIniKey(sectionString, "B", "/", _keyboard["B"]);
|
|
}
|
|
break;
|
|
|
|
case Hardware:
|
|
{
|
|
scanCodeFromIniKey(sectionString, "Reset", "CTRL+F2", _hardware["Reset"]);
|
|
}
|
|
break;
|
|
|
|
case Debugger:
|
|
{
|
|
scanCodeFromIniKey(sectionString, "Debug", "CTRL+F6", _debugger["Debug"]);
|
|
scanCodeFromIniKey(sectionString, "RunToBrk", "CTRL+F7", _debugger["RunToBrk"]);
|
|
scanCodeFromIniKey(sectionString, "StepPC", "CTRL+F8", _debugger["StepPC"]);
|
|
scanCodeFromIniKey(sectionString, "StepWatch", "CTRL+F9", _debugger["StepWatch"]);
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void resetBrowserList(void)
|
|
{
|
|
_cursorX = 0;
|
|
_cursorY = 0;
|
|
_fileEntriesIndex = 0;
|
|
_fileEntriesSize = int(_fileEntries.size());
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
bool isWin32Drive(const std::string& path)
|
|
{
|
|
if(path.size() == 3 && path[path.size() - 2] == ':' && (path[path.size() - 1] == '/' || path[path.size() - 1] == '\\')) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool browseWin32Drives(void)
|
|
{
|
|
DWORD size = MAX_PATH;
|
|
char drives[MAX_PATH] = {0};
|
|
DWORD result = GetLogicalDriveStrings(size, drives);
|
|
|
|
if(result > 0 && result <= MAX_PATH)
|
|
{
|
|
_fileEntries.clear();
|
|
|
|
char* drive = drives;
|
|
while(*drive)
|
|
{
|
|
FileEntry fileEntry = {Dir, std::string(drive)};
|
|
_fileEntries.push_back(fileEntry);
|
|
|
|
//fprintf(stderr, "%s\n", drive);
|
|
drive += strlen(drive) + 1;
|
|
}
|
|
|
|
resetBrowserList();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool backOneDirectory(void)
|
|
{
|
|
#ifdef _WIN32
|
|
if(isWin32Drive(_browserPath))
|
|
{
|
|
if(browseWin32Drives()) return false;
|
|
}
|
|
#endif
|
|
|
|
size_t slash = _browserPath.find_last_of("\\/", _browserPath.size() - 2);
|
|
if(slash != std::string::npos)
|
|
{
|
|
_browserPath = _browserPath.substr(0, slash + 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void browseDirectory(void)
|
|
{
|
|
const std::vector<std::string> suffixes = {".gbas", ".gtb", ".gcl", ".gasm", ".vasm", ".gt1"};
|
|
|
|
browseDirectory(suffixes);
|
|
}
|
|
|
|
void browseDirectory(const std::vector<std::string>& suffixes)
|
|
{
|
|
//fprintf(stderr, "%s\n", _browserPath.c_str());
|
|
|
|
std::string path = _browserPath + ".";
|
|
Assembler::setIncludePath(_browserPath);
|
|
|
|
_fileEntries.clear();
|
|
|
|
DIR *dir;
|
|
struct dirent *ent;
|
|
std::vector<std::string> dirnames;
|
|
dirnames.push_back("..");
|
|
std::vector<std::string> filenames;
|
|
if((dir = opendir(path.c_str())) != NULL)
|
|
{
|
|
while((ent = readdir(dir)) != NULL)
|
|
{
|
|
std::string filename = std::string(ent->d_name);
|
|
size_t nonWhiteSpace = filename.find_first_not_of(" \n\r\f\t\v");
|
|
if(ent->d_type == DT_DIR && filename[0] != '.' && filename.find("$RECYCLE") == std::string::npos && nonWhiteSpace != std::string::npos)
|
|
{
|
|
dirnames.push_back(filename);
|
|
}
|
|
else if(ent->d_type == DT_REG)
|
|
{
|
|
for(int i=0; i<int(suffixes.size()); i++)
|
|
{
|
|
std::string name = Expression::strUpper(filename);
|
|
std::string suffix = Expression::strUpper(suffixes[i]);
|
|
if(name.find(suffix) != std::string::npos)
|
|
{
|
|
filenames.push_back(filename);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir (dir);
|
|
}
|
|
|
|
// .. is always first
|
|
std::sort(dirnames.begin() + 1, dirnames.end());
|
|
for(int i=0; i<int(dirnames.size()); i++)
|
|
{
|
|
FileEntry fileEntry = {Dir, dirnames[i]};
|
|
_fileEntries.push_back(fileEntry);
|
|
}
|
|
|
|
std::sort(filenames.begin(), filenames.end());
|
|
for(int i=0; i<int(filenames.size()); i++)
|
|
{
|
|
FileEntry fileEntry = {File, filenames[i]};
|
|
_fileEntries.push_back(fileEntry);
|
|
}
|
|
|
|
// Only reset browser list if list size has changed
|
|
if(_fileEntriesSize != int(_fileEntries.size())) resetBrowserList();
|
|
}
|
|
|
|
void changeBrowseDirectory(void)
|
|
{
|
|
std::string entry = *getCurrentFileEntryName();
|
|
|
|
// Parent dir
|
|
if(entry == "..")
|
|
{
|
|
// Don't browse current directory for Win32 logical drive list
|
|
if(!backOneDirectory()) return;
|
|
}
|
|
#ifdef _WIN32
|
|
// Win32 logical drive
|
|
else if(isWin32Drive(entry))
|
|
{
|
|
_browserPath = entry;
|
|
}
|
|
#endif
|
|
// Directory
|
|
else
|
|
{
|
|
_browserPath += entry + "/";
|
|
}
|
|
|
|
browseDirectory();
|
|
}
|
|
|
|
void handleBrowsePageUp(uint16_t numRows)
|
|
{
|
|
if((_fileEntriesIndex -= numRows) < 0) _fileEntriesIndex = 0;
|
|
}
|
|
|
|
void handleBrowsePageDown(uint16_t numRows)
|
|
{
|
|
if(_fileEntries.size() > HEX_CHARS_Y)
|
|
{
|
|
_fileEntriesIndex += numRows;
|
|
int fileEntriesDelta = int(_fileEntries.size()) - _fileEntriesIndex;
|
|
if(fileEntriesDelta < HEX_CHARS_Y) _fileEntriesIndex -= HEX_CHARS_Y - std::max(fileEntriesDelta, 0);
|
|
}
|
|
}
|
|
|
|
void handlePageUp(uint16_t numRows)
|
|
{
|
|
switch(_editorMode)
|
|
{
|
|
case Hex: setHexBaseAddress((_memoryMode == RAM) ? (getHexBaseAddress() - HEX_CHARS_X*numRows) & (Memory::getSizeRAM()-1) : getHexBaseAddress() - HEX_CHARS_X*numRows); break;
|
|
case Load: if((_fileEntriesIndex -= numRows) < 0) _fileEntriesIndex = 0; break;
|
|
case Rom: if((_romEntriesIndex -= numRows) < 0) _romEntriesIndex = 0; break;
|
|
|
|
case Dasm:
|
|
{
|
|
if(numRows == 1)
|
|
{
|
|
if(_memoryMode == RAM)
|
|
{
|
|
_vpcBaseAddress = uint16_t(_vpcBaseAddress - Assembler::getPrevDasmByteCount()) & (Memory::getSizeRAM()-1);
|
|
}
|
|
else
|
|
{
|
|
_ntvBaseAddress = uint16_t(_ntvBaseAddress - Assembler::getPrevDasmByteCount());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(_memoryMode == RAM)
|
|
{
|
|
_vpcBaseAddress = uint16_t(_vpcBaseAddress - Assembler::getPrevDasmPageByteCount()) & (Memory::getSizeRAM()-1);
|
|
}
|
|
else
|
|
{
|
|
_ntvBaseAddress = uint16_t(_ntvBaseAddress - Assembler::getPrevDasmPageByteCount());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void handlePageDown(uint16_t numRows)
|
|
{
|
|
switch(_editorMode)
|
|
{
|
|
case Hex: setHexBaseAddress((_memoryMode == RAM) ? (getHexBaseAddress() + HEX_CHARS_X*numRows) & (Memory::getSizeRAM()-1) : getHexBaseAddress() + HEX_CHARS_X*numRows); break;
|
|
|
|
case Load:
|
|
{
|
|
if(_fileEntries.size() > HEX_CHARS_Y)
|
|
{
|
|
_fileEntriesIndex += numRows;
|
|
int fileEntriesDelta = int(_fileEntries.size()) - _fileEntriesIndex;
|
|
if(fileEntriesDelta < HEX_CHARS_Y) _fileEntriesIndex -= HEX_CHARS_Y - std::max(fileEntriesDelta, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Rom:
|
|
{
|
|
if(_romEntries.size() > HEX_CHARS_Y)
|
|
{
|
|
_romEntriesIndex += numRows;
|
|
int romEntriesDelta = int(_romEntries.size()) - _romEntriesIndex;
|
|
if(romEntriesDelta < HEX_CHARS_Y) _romEntriesIndex -= HEX_CHARS_Y - std::max(romEntriesDelta, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Dasm:
|
|
{
|
|
if(numRows == 1)
|
|
{
|
|
if(_memoryMode == RAM)
|
|
{
|
|
_vpcBaseAddress = uint16_t(_vpcBaseAddress + Assembler::getCurrDasmByteCount()) & (Memory::getSizeRAM()-1);
|
|
}
|
|
else
|
|
{
|
|
_ntvBaseAddress = uint16_t(_ntvBaseAddress + Assembler::getCurrDasmByteCount());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(_memoryMode == RAM)
|
|
{
|
|
_vpcBaseAddress = uint16_t(_vpcBaseAddress + Assembler::getCurrDasmPageByteCount()) & (Memory::getSizeRAM()-1);
|
|
}
|
|
else
|
|
{
|
|
_ntvBaseAddress = uint16_t(_ntvBaseAddress + Assembler::getCurrDasmPageByteCount());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void handleMouseWheel(const SDL_Event& event)
|
|
{
|
|
if(event.wheel.y > 0) handlePageUp(1);
|
|
if(event.wheel.y < 0) handlePageDown(1);
|
|
}
|
|
|
|
void handleMouseLeftClick(void)
|
|
{
|
|
if(_editorMode == Hex || (_cursorY < 0 && _editorMode != Rom))
|
|
{
|
|
_hexEdit = !_hexEdit;
|
|
if(!_hexEdit) resetEditor();
|
|
}
|
|
else if(_editorMode == Load)
|
|
{
|
|
// No loading/browsing if cursor is out of bounds
|
|
if(_cursorY >= getFileEntriesSize()) return;
|
|
|
|
FileType fileType = getCurrentFileEntryType();
|
|
switch(fileType)
|
|
{
|
|
case File: Loader::uploadDirect(Loader::Emulator); break;
|
|
case Dir: changeBrowseDirectory(); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
else if(_editorMode == Rom)
|
|
{
|
|
if(_cursorY < 0 || _cursorY >= getRomEntriesSize()) return;
|
|
|
|
Cpu::loadRom(getCurrentRomEntryIndex());
|
|
}
|
|
else if(_editorMode == Dasm && _singleStepEnabled)
|
|
{
|
|
// Validate breakpoint
|
|
if(_cursorY < 0 || _cursorY >= Assembler::getDisassembledCodeSize()) return;
|
|
|
|
// If breakpoint already exists, delete it, otherwise save it
|
|
if(_memoryMode == RAM)
|
|
{
|
|
auto it = std::find(_vpcBreakPoints.begin(), _vpcBreakPoints.end(), Assembler::getDisassembledCode(_cursorY)->_address);
|
|
if(it != _vpcBreakPoints.end())
|
|
{
|
|
_vpcBreakPoints.erase(it);
|
|
}
|
|
else
|
|
{
|
|
_vpcBreakPoints.push_back(Assembler::getDisassembledCode(_cursorY)->_address);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto it = std::find(_ntvBreakPoints.begin(), _ntvBreakPoints.end(), Assembler::getDisassembledCode(_cursorY)->_address);
|
|
if(it != _ntvBreakPoints.end())
|
|
{
|
|
_ntvBreakPoints.erase(it);
|
|
}
|
|
else
|
|
{
|
|
_ntvBreakPoints.push_back(Assembler::getDisassembledCode(_cursorY)->_address);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void handleMouseRightClick(void)
|
|
{
|
|
if(_editorMode == Load)
|
|
{
|
|
// No loading/browsing if cursor is out of bounds
|
|
if(_cursorY >= getFileEntriesSize()) return;
|
|
|
|
FileType fileType = getCurrentFileEntryType();
|
|
switch(fileType)
|
|
{
|
|
case File: Loader::uploadDirect(Loader::Hardware); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
OnVarType updateOnVarType(void)
|
|
{
|
|
if(_singleStepEnabled)
|
|
{
|
|
if(getCursorY() == -2 && getCursorX() > 5 && getCursorX() < 7) return OnWatch;
|
|
}
|
|
else
|
|
{
|
|
if(getCursorY() == -2 && getCursorX() > 3 && getCursorX() < 6) return OnCpuA;
|
|
if(getCursorY() == -2 && getCursorX() > 5 && getCursorX() < 8) return OnCpuB;
|
|
}
|
|
if(getCursorY() == -1 && getCursorX() > 0 && getCursorX() < 3) return OnHex;
|
|
if(getCursorY() == -1 && getCursorX() > 4 && getCursorX() < 7) return OnVars;
|
|
|
|
return OnNone;
|
|
}
|
|
|
|
void updateEditor(void)
|
|
{
|
|
if(!_hexEdit)
|
|
{
|
|
resetEditor();
|
|
return;
|
|
}
|
|
|
|
// Don't allow emulator commands to accidently modify fields
|
|
if(_sdlKeyModifier == KMOD_LALT || _sdlKeyModifier == KMOD_LCTRL) return;
|
|
|
|
int range = 0;
|
|
if(_sdlKeyScanCode >= SDLK_0 && _sdlKeyScanCode <= SDLK_9) range = 1;
|
|
if(_sdlKeyScanCode >= SDLK_a && _sdlKeyScanCode <= SDLK_f) range = 2;
|
|
if(range == 1 || range == 2)
|
|
{
|
|
uint16_t value = 0;
|
|
switch(range)
|
|
{
|
|
case 1: value = uint16_t(_sdlKeyScanCode - SDLK_0); break;
|
|
case 2: value = uint16_t(_sdlKeyScanCode - SDLK_a + 10); break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
// Edit memory
|
|
if(_memoryMode == RAM && _cursorY >= 0)
|
|
{
|
|
uint16_t address = uint16_t(getHexBaseAddress() + _cursorX + _cursorY*HEX_CHARS_X);
|
|
switch(_memoryDigit)
|
|
{
|
|
case 0: value = (value << 4) & 0xF0; Cpu::setRAM(address, uint8_t((Cpu::getRAM(address) & 0x0F) | value)); break;
|
|
case 1: value = (value << 0) & 0x0F; Cpu::setRAM(address, uint8_t((Cpu::getRAM(address) & 0xF0) | value)); break;
|
|
|
|
default: break;
|
|
}
|
|
_memoryDigit = (_memoryDigit + 1) & 0x01;
|
|
return;
|
|
}
|
|
|
|
// Edit variables
|
|
switch(_onVarType)
|
|
{
|
|
case OnCpuA:
|
|
{
|
|
switch(_addressDigit)
|
|
{
|
|
case 0: value = (value << 12) & 0xF000; _cpuUsageAddressA = (_cpuUsageAddressA & 0x0FFF) | value; break;
|
|
case 1: value = (value << 8) & 0x0F00; _cpuUsageAddressA = (_cpuUsageAddressA & 0xF0FF) | value; break;
|
|
case 2: value = (value << 4) & 0x00F0; _cpuUsageAddressA = (_cpuUsageAddressA & 0xFF0F) | value; break;
|
|
case 3: value = (value << 0) & 0x000F; _cpuUsageAddressA = (_cpuUsageAddressA & 0xFFF0) | value; break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OnCpuB:
|
|
{
|
|
switch(_addressDigit)
|
|
{
|
|
case 0: value = (value << 12) & 0xF000; _cpuUsageAddressB = (_cpuUsageAddressB & 0x0FFF) | value; break;
|
|
case 1: value = (value << 8) & 0x0F00; _cpuUsageAddressB = (_cpuUsageAddressB & 0xF0FF) | value; break;
|
|
case 2: value = (value << 4) & 0x00F0; _cpuUsageAddressB = (_cpuUsageAddressB & 0xFF0F) | value; break;
|
|
case 3: value = (value << 0) & 0x000F; _cpuUsageAddressB = (_cpuUsageAddressB & 0xFFF0) | value; break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OnHex:
|
|
{
|
|
// Hex address field is disabled for ROM browsing
|
|
switch(_editorMode)
|
|
{
|
|
case Load:
|
|
{
|
|
switch(_addressDigit)
|
|
{
|
|
case 0: value = (value << 12) & 0xF000; _loadBaseAddress = (_loadBaseAddress & 0x0FFF) | value; break;
|
|
case 1: value = (value << 8) & 0x0F00; _loadBaseAddress = (_loadBaseAddress & 0xF0FF) | value; break;
|
|
case 2: value = (value << 4) & 0x00F0; _loadBaseAddress = (_loadBaseAddress & 0xFF0F) | value; break;
|
|
case 3: value = (value << 0) & 0x000F; _loadBaseAddress = (_loadBaseAddress & 0xFFF0) | value; break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
if(_loadBaseAddress < LOAD_BASE_ADDRESS) _loadBaseAddress = LOAD_BASE_ADDRESS;
|
|
}
|
|
break;
|
|
|
|
case Dasm:
|
|
{
|
|
switch(_memoryMode)
|
|
{
|
|
case RAM:
|
|
{
|
|
switch(_addressDigit)
|
|
{
|
|
case 0: value = (value << 12) & 0xF000; _vpcBaseAddress = (_vpcBaseAddress & 0x0FFF) | value; break;
|
|
case 1: value = (value << 8) & 0x0F00; _vpcBaseAddress = (_vpcBaseAddress & 0xF0FF) | value; break;
|
|
case 2: value = (value << 4) & 0x00F0; _vpcBaseAddress = (_vpcBaseAddress & 0xFF0F) | value; break;
|
|
case 3: value = (value << 0) & 0x000F; _vpcBaseAddress = (_vpcBaseAddress & 0xFFF0) | value; break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
switch(_addressDigit)
|
|
{
|
|
case 0: value = (value << 12) & 0xF000; _ntvBaseAddress = (_ntvBaseAddress & 0x0FFF) | value; break;
|
|
case 1: value = (value << 8) & 0x0F00; _ntvBaseAddress = (_ntvBaseAddress & 0xF0FF) | value; break;
|
|
case 2: value = (value << 4) & 0x00F0; _ntvBaseAddress = (_ntvBaseAddress & 0xFF0F) | value; break;
|
|
case 3: value = (value << 0) & 0x000F; _ntvBaseAddress = (_ntvBaseAddress & 0xFFF0) | value; break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Hex:
|
|
{
|
|
switch(_addressDigit)
|
|
{
|
|
case 0: value = (value << 12) & 0xF000; setHexBaseAddress((getHexBaseAddress() & 0x0FFF) | value); break;
|
|
case 1: value = (value << 8) & 0x0F00; setHexBaseAddress((getHexBaseAddress() & 0xF0FF) | value); break;
|
|
case 2: value = (value << 4) & 0x00F0; setHexBaseAddress((getHexBaseAddress() & 0xFF0F) | value); break;
|
|
case 3: value = (value << 0) & 0x000F; setHexBaseAddress((getHexBaseAddress() & 0xFFF0) | value); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OnVars:
|
|
{
|
|
switch(_addressDigit)
|
|
{
|
|
case 0: value = (value << 12) & 0xF000; _varsBaseAddress = (_varsBaseAddress & 0x0FFF) | value; break;
|
|
case 1: value = (value << 8) & 0x0F00; _varsBaseAddress = (_varsBaseAddress & 0xF0FF) | value; break;
|
|
case 2: value = (value << 4) & 0x00F0; _varsBaseAddress = (_varsBaseAddress & 0xFF0F) | value; break;
|
|
case 3: value = (value << 0) & 0x000F; _varsBaseAddress = (_varsBaseAddress & 0xFFF0) | value; break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OnWatch:
|
|
{
|
|
switch(_addressDigit)
|
|
{
|
|
case 0: value = (value << 12) & 0xF000; _singleStepAddress = (_singleStepAddress & 0x0FFF) | value; break;
|
|
case 1: value = (value << 8) & 0x0F00; _singleStepAddress = (_singleStepAddress & 0xF0FF) | value; break;
|
|
case 2: value = (value << 4) & 0x00F0; _singleStepAddress = (_singleStepAddress & 0xFF0F) | value; break;
|
|
case 3: value = (value << 0) & 0x000F; _singleStepAddress = (_singleStepAddress & 0xFFF0) | value; break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: return;
|
|
}
|
|
|
|
_addressDigit = (_addressDigit + 1) & 0x03;
|
|
}
|
|
}
|
|
|
|
|
|
void startDebugger(void)
|
|
{
|
|
_singleStep = false;
|
|
_singleStepEnabled = true;
|
|
_singleStepMode = RunToBrk;
|
|
|
|
_ntvBaseAddress = Cpu::getStateS()._PC;
|
|
_vpcBaseAddress = Cpu::getVPC();
|
|
}
|
|
|
|
void resetDebugger(void)
|
|
{
|
|
_singleStep = false;
|
|
_singleStepEnabled = false;
|
|
_singleStepMode = RunToBrk;
|
|
|
|
Audio::clearQueue();
|
|
}
|
|
|
|
void beginStep(uint16_t address)
|
|
{
|
|
_singleStep = false;
|
|
_singleStepEnabled = true;
|
|
if(_memoryMode != RAM) _ntvBaseAddress = address;
|
|
if(_memoryMode == RAM) _vpcBaseAddress = address;
|
|
}
|
|
|
|
void runToBreakpoint(void)
|
|
{
|
|
_singleStep = true;
|
|
_singleStepEnabled = false;
|
|
_singleStepMode = RunToBrk;
|
|
_singleStepTicks = SDL_GetTicks();
|
|
}
|
|
|
|
void singleStepWatch(void)
|
|
{
|
|
_singleStep = true;
|
|
_singleStepEnabled = false;
|
|
_singleStepMode = StepWatch;
|
|
_singleStepTicks = SDL_GetTicks();
|
|
_singleStepVpc = Cpu::getRAM(_singleStepAddress);
|
|
_singleStepNtv = Cpu::getRAM(_singleStepAddress);
|
|
}
|
|
|
|
void singleStepPc(void)
|
|
{
|
|
_singleStep = true;
|
|
_singleStepEnabled = false;
|
|
_singleStepMode = StepPC;
|
|
_singleStepTicks = SDL_GetTicks();
|
|
_singleStepVpc = Cpu::getVPC();
|
|
_singleStepNtv = Cpu::getStateS()._PC;
|
|
}
|
|
|
|
|
|
// PS2 Keyboard emulation mode
|
|
bool handlePs2KeyDown(void)
|
|
{
|
|
if(_keyboardMode == PS2 || _keyboardMode == HwPS2)
|
|
{
|
|
switch(_sdlKeyScanCode)
|
|
{
|
|
case SDLK_LEFT: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_LEFT, true) : Cpu::setIN(Cpu::getIN() & ~INPUT_LEFT ); return true;
|
|
case SDLK_RIGHT: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_RIGHT, true) : Cpu::setIN(Cpu::getIN() & ~INPUT_RIGHT ); return true;
|
|
case SDLK_UP: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_UP, true) : Cpu::setIN(Cpu::getIN() & ~INPUT_UP ); return true;
|
|
case SDLK_DOWN: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_DOWN, true) : Cpu::setIN(Cpu::getIN() & ~INPUT_DOWN ); return true;
|
|
case SDLK_PAGEUP: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_START, true) : Cpu::setIN(Cpu::getIN() & ~INPUT_START ); return true;
|
|
case SDLK_PAGEDOWN: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_SELECT, true) : Cpu::setIN(Cpu::getIN() & ~INPUT_SELECT); return true;
|
|
|
|
default: break;
|
|
}
|
|
|
|
if((_sdlKeyScanCode >= 0 && _sdlKeyScanCode <= 31) || _sdlKeyScanCode == 127 || _sdlKeyScanCode == 'c')
|
|
{
|
|
switch(_sdlKeyScanCode)
|
|
{
|
|
case SDLK_TAB: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_INPUT_A, true) : Cpu::setIN(Cpu::getIN() & ~INPUT_A); return true;
|
|
case SDLK_ESCAPE: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_INPUT_B, true) : Cpu::setIN(Cpu::getIN() & ~INPUT_B); return true;
|
|
case SDLK_RETURN: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_CR, true) : Cpu::setIN('\n'); return true;
|
|
|
|
case SDLK_DELETE:
|
|
case SDLK_BACKSPACE: (_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_DEL, true) : Cpu::setIN(127); return true;
|
|
|
|
case 'c':
|
|
{
|
|
if(_sdlKeyModifier == KMOD_LCTRL)
|
|
{
|
|
(_keyboardMode == HwPS2) ? (void)Loader::sendCommandToGiga(HW_PS2_CTLR_C, true) : Cpu::setIN(HW_PS2_CTLR_C);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
// Handle normal keys
|
|
if(_sdlKeyScanCode >= 32 && _sdlKeyScanCode <= 126)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Gigatron Keyboard emulation mode
|
|
bool handleGigaKeyDown(void)
|
|
{
|
|
if(_keyboardMode == Giga)
|
|
{
|
|
if(_sdlKeyScanCode == _keyboard["Left"]._scanCode && _sdlKeyModifier == _keyboard["Left"]._keyMod) {Cpu::setIN(Cpu::getIN() & ~INPUT_LEFT); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Right"]._scanCode && _sdlKeyModifier == _keyboard["Right"]._keyMod) {Cpu::setIN(Cpu::getIN() & ~INPUT_RIGHT); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Up"]._scanCode && _sdlKeyModifier == _keyboard["Up"]._keyMod) {Cpu::setIN(Cpu::getIN() & ~INPUT_UP); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Down"]._scanCode && _sdlKeyModifier == _keyboard["Down"]._keyMod) {Cpu::setIN(Cpu::getIN() & ~INPUT_DOWN); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Start"]._scanCode && _sdlKeyModifier == _keyboard["Start"]._keyMod) {Cpu::setIN(Cpu::getIN() & ~INPUT_START); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Select"]._scanCode && _sdlKeyModifier == _keyboard["Select"]._keyMod) {Cpu::setIN(Cpu::getIN() & ~INPUT_SELECT); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["A"]._scanCode && _sdlKeyModifier == _keyboard["A"]._keyMod) {Cpu::setIN(Cpu::getIN() & ~INPUT_A); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["B"]._scanCode && _sdlKeyModifier == _keyboard["B"]._keyMod) {Cpu::setIN(Cpu::getIN() & ~INPUT_B); return true;}
|
|
}
|
|
else if(_keyboardMode == HwGiga)
|
|
{
|
|
if(_sdlKeyScanCode == _keyboard["Left"]._scanCode && _sdlKeyModifier == _keyboard["Left"]._keyMod) {Loader::sendCommandToGiga('A', true); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Right"]._scanCode && _sdlKeyModifier == _keyboard["Right"]._keyMod) {Loader::sendCommandToGiga('D', true); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Up"]._scanCode && _sdlKeyModifier == _keyboard["Up"]._keyMod) {Loader::sendCommandToGiga('W', true); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Down"]._scanCode && _sdlKeyModifier == _keyboard["Down"]._keyMod) {Loader::sendCommandToGiga('S', true); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Start"]._scanCode && _sdlKeyModifier == _keyboard["Start"]._keyMod) {Loader::sendCommandToGiga('E', true); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Select"]._scanCode && _sdlKeyModifier == _keyboard["Select"]._keyMod) {Loader::sendCommandToGiga('Q', true); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["A"]._scanCode && _sdlKeyModifier == _keyboard["A"]._keyMod) {Loader::sendCommandToGiga('Z', true); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["B"]._scanCode && _sdlKeyModifier == _keyboard["B"]._keyMod) {Loader::sendCommandToGiga('X', true); return true;}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
int gtRgbFileindex = 0;
|
|
std::vector<std::string> names = {"lowres", "turrican", "clouds1", "sunset", "mario", "juggler", "venus", "forest", "doom", "MonaLisa", "parallax"};
|
|
|
|
void loadGtRgbFile(void)
|
|
{
|
|
Image::TgaFile tgaFile;
|
|
Image::loadTgaFile("images/" + names[gtRgbFileindex] + ".tga", tgaFile);
|
|
|
|
std::vector<uint8_t> data;
|
|
std::vector<uint16_t> optional;
|
|
Image::GtRgbFile gtRgbFile{GTRGB_IDENTIFIER, Image::GT_RGB_222, tgaFile._header._width, tgaFile._header._height, data, optional};
|
|
Image::ditherRGB8toRGB2(tgaFile._data, gtRgbFile._data, tgaFile._header._width, tgaFile._header._height, tgaFile._imageOrigin);
|
|
|
|
if(gtRgbFile._header._width > 256)
|
|
{
|
|
fprintf(stderr, "Editor::loadGtRgbFile() : Width > 256, (%d, %d), in %s\n", gtRgbFile._header._width, gtRgbFile._header._height, names[gtRgbFileindex].c_str());
|
|
return;
|
|
}
|
|
|
|
uint16_t vram = 0x0800;
|
|
for(int y=0; y<gtRgbFile._header._height; y++)
|
|
{
|
|
for(int x=0; x<gtRgbFile._header._width; x++)
|
|
{
|
|
Cpu::setRAM(vram++, gtRgbFile._data[y*gtRgbFile._header._width + x]);
|
|
}
|
|
vram += 256 - gtRgbFile._header._width;
|
|
}
|
|
}
|
|
|
|
void loadNextGtRgbFile(void)
|
|
{
|
|
gtRgbFileindex = (gtRgbFileindex + 1) % names.size();
|
|
loadGtRgbFile();
|
|
}
|
|
|
|
|
|
void handleKeyDown(bool gigaKeyValid)
|
|
{
|
|
//fprintf(stderr, "Editor::handleKeyDown() : key=%d : mod=%04x\n", _sdlKeyScanCode, _sdlKeyModifier);
|
|
|
|
if(_sdlKeyScanCode == _emulator["Quit"]._scanCode && _sdlKeyModifier == _emulator["Quit"]._keyMod)
|
|
{
|
|
Cpu::shutdown();
|
|
exit(0);
|
|
}
|
|
// Terminal mode
|
|
else if(_sdlKeyScanCode == _emulator["Terminal"]._scanCode && _sdlKeyModifier == _emulator["Terminal"]._keyMod)
|
|
{
|
|
Terminal::switchToTerminal();
|
|
}
|
|
// Image editor
|
|
else if(_sdlKeyScanCode == _emulator["ImageEditor"]._scanCode && _sdlKeyModifier == _emulator["ImageEditor"]._keyMod)
|
|
{
|
|
setEditorMode(Image);
|
|
}
|
|
// Audio editor
|
|
else if(_sdlKeyScanCode == _emulator["AudioEditor"]._scanCode && _sdlKeyModifier == _emulator["AudioEditor"]._keyMod)
|
|
{
|
|
setEditorMode(Audio);
|
|
}
|
|
// Emulator reset
|
|
else if(_sdlKeyScanCode == _emulator["Reset"]._scanCode && _sdlKeyModifier == _emulator["Reset"]._keyMod)
|
|
{
|
|
Cpu::enable6BitSound(Cpu::ROMv5a, false);
|
|
resetDebugger();
|
|
Cpu::reset();
|
|
return;
|
|
}
|
|
// Hardware reset
|
|
else if(_sdlKeyScanCode == _hardware["Reset"]._scanCode && _sdlKeyModifier == _hardware["Reset"]._keyMod)
|
|
{
|
|
Loader::sendCommandToGiga('R', false);
|
|
return;
|
|
}
|
|
// Scanline handler
|
|
else if(_sdlKeyScanCode == _emulator["ScanlineMode"]._scanCode && _sdlKeyModifier == _emulator["ScanlineMode"]._keyMod)
|
|
{
|
|
// ROMS after v1 have their own inbuilt scanline handlers
|
|
if(Cpu::getRomType() != Cpu::ROMv1)
|
|
{
|
|
Cpu::setIN(Cpu::getIN() & ~INPUT_SELECT);
|
|
return;
|
|
}
|
|
}
|
|
// PS2 Keyboard emulation mode
|
|
else if(gigaKeyValid && handlePs2KeyDown())
|
|
{
|
|
return;
|
|
}
|
|
// Gigatron Keyboard emulation mode
|
|
else if(gigaKeyValid && handleGigaKeyDown())
|
|
{
|
|
return;
|
|
}
|
|
#if 1
|
|
else if(_sdlKeyScanCode == SDLK_PAGEUP)
|
|
{
|
|
double gamma = Image::getGammaInput();
|
|
gamma += 0.1; if(gamma > 3.0) gamma = 3.0;
|
|
Image::setGammaInput(gamma);
|
|
fprintf(stderr, "GammaInput = %f\n", gamma);
|
|
loadGtRgbFile();
|
|
}
|
|
else if(_sdlKeyScanCode == SDLK_PAGEDOWN)
|
|
{
|
|
double gamma = Image::getGammaInput();
|
|
gamma -= 0.1; if(gamma < 0.5) gamma = 0.5;
|
|
Image::setGammaInput(gamma);
|
|
fprintf(stderr, "GammaInput = %f\n", gamma);
|
|
loadGtRgbFile();
|
|
}
|
|
else if(_sdlKeyScanCode == SDLK_INSERT)
|
|
{
|
|
Image::setGammaOutput(Image::getGammaOutput() + 0.1);
|
|
fprintf(stderr, "GammaOutput = %f\n", Image::getGammaOutput());
|
|
loadGtRgbFile();
|
|
}
|
|
else if(_sdlKeyScanCode == SDLK_DELETE)
|
|
{
|
|
Image::setGammaOutput(Image::getGammaOutput() - 0.1);
|
|
fprintf(stderr, "GammaOutput = %f\n", Image::getGammaOutput());
|
|
loadGtRgbFile();
|
|
}
|
|
else if(_sdlKeyScanCode == SDLK_END)
|
|
{
|
|
Image::setDiffusionScale(Image::getDiffusionScale() * 2.0);
|
|
fprintf(stderr, "DiffusionScale = %f\n", Image::getDiffusionScale());
|
|
loadGtRgbFile();
|
|
}
|
|
else if(_sdlKeyScanCode == SDLK_HOME)
|
|
{
|
|
loadNextGtRgbFile();
|
|
}
|
|
else if(_sdlKeyScanCode == SDLK_BACKSLASH)
|
|
{
|
|
Image::setDiffusionType(Image::getDiffusionType() + 1);
|
|
fprintf(stderr, "DiffusionType = %d\n", Image::getDiffusionType());
|
|
loadGtRgbFile();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// PS2 Keyboard emulation mode
|
|
bool handlePs2KeyUp(void)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Gigatron Keyboard emulation mode
|
|
bool handleGigaKeyUp(void)
|
|
{
|
|
if(_keyboardMode == Giga)
|
|
{
|
|
if(_sdlKeyScanCode == _keyboard["Left"]._scanCode && _sdlKeyModifier == _keyboard["Left"]._keyMod) {Cpu::setIN(Cpu::getIN() | INPUT_LEFT); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Right"]._scanCode && _sdlKeyModifier == _keyboard["Right"]._keyMod) {Cpu::setIN(Cpu::getIN() | INPUT_RIGHT); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Up"]._scanCode && _sdlKeyModifier == _keyboard["Up"]._keyMod) {Cpu::setIN(Cpu::getIN() | INPUT_UP); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Down"]._scanCode && _sdlKeyModifier == _keyboard["Down"]._keyMod) {Cpu::setIN(Cpu::getIN() | INPUT_DOWN); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Start"]._scanCode && _sdlKeyModifier == _keyboard["Start"]._keyMod) {Cpu::setIN(Cpu::getIN() | INPUT_START); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["Select"]._scanCode && _sdlKeyModifier == _keyboard["Select"]._keyMod) {Cpu::setIN(Cpu::getIN() | INPUT_SELECT); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["A"]._scanCode && _sdlKeyModifier == _keyboard["A"]._keyMod) {Cpu::setIN(Cpu::getIN() | INPUT_A); return true;}
|
|
else if(_sdlKeyScanCode == _keyboard["B"]._scanCode && _sdlKeyModifier == _keyboard["B"]._keyMod) {Cpu::setIN(Cpu::getIN() | INPUT_B); return true;}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void handleKeyUp(bool gigaKeyValid)
|
|
{
|
|
// Toggle help screen
|
|
if(_sdlKeyScanCode == _emulator["Help"]._scanCode && _sdlKeyModifier == _emulator["Help"]._keyMod)
|
|
{
|
|
static bool helpScreen = false;
|
|
helpScreen = !helpScreen;
|
|
Graphics::setDisplayHelpScreen(helpScreen);
|
|
}
|
|
// Disassembler
|
|
else if(_sdlKeyScanCode == _emulator["Disassembler"]._scanCode && _sdlKeyModifier == _emulator["Disassembler"]._keyMod)
|
|
{
|
|
if(_editorMode == Dasm)
|
|
{
|
|
_editorMode = _editorModePrev;
|
|
_editorModePrev = Dasm;
|
|
}
|
|
else
|
|
{
|
|
_editorModePrev = _editorMode;
|
|
_editorMode = Dasm;
|
|
}
|
|
}
|
|
// ROMS after v1 have their own inbuilt scanline handlers
|
|
else if(_sdlKeyScanCode == _emulator["ScanlineMode"]._scanCode && _sdlKeyModifier == _emulator["ScanlineMode"]._keyMod)
|
|
{
|
|
(Cpu::getRomType() != Cpu::ROMv1) ? Cpu::setIN(Cpu::getIN() | INPUT_SELECT) : Cpu::swapScanlineMode();
|
|
}
|
|
// Browse vCPU directory
|
|
else if(_sdlKeyScanCode == _emulator["Browse"]._scanCode && _sdlKeyModifier == _emulator["Browse"]._keyMod)
|
|
{
|
|
if(_editorMode == Load)
|
|
{
|
|
_editorMode = _editorModePrev;
|
|
_editorModePrev = Load;
|
|
}
|
|
else
|
|
{
|
|
_editorModePrev = _editorMode;
|
|
_editorMode = Load;
|
|
browseDirectory();
|
|
}
|
|
}
|
|
// ROM type
|
|
else if(_sdlKeyScanCode == _emulator["RomType"]._scanCode && _sdlKeyModifier == _emulator["RomType"]._keyMod)
|
|
{
|
|
if(_editorMode == Rom)
|
|
{
|
|
_editorMode = _editorModePrev;
|
|
_editorModePrev = Rom;
|
|
}
|
|
else
|
|
{
|
|
_editorModePrev = _editorMode;
|
|
_editorMode = Rom;
|
|
_hexEdit = false;
|
|
resetEditor();
|
|
}
|
|
}
|
|
// Hex monitor
|
|
else if(_sdlKeyScanCode == _emulator["HexMonitor"]._scanCode && _sdlKeyModifier == _emulator["HexMonitor"]._keyMod)
|
|
{
|
|
if(_editorMode == Hex)
|
|
{
|
|
_editorMode = _editorModePrev;
|
|
_editorModePrev = Hex;
|
|
}
|
|
else
|
|
{
|
|
_editorModePrev = _editorMode;
|
|
_editorMode = Hex;
|
|
}
|
|
}
|
|
// Debug mode
|
|
else if(_sdlKeyScanCode == _debugger["Debug"]._scanCode && _sdlKeyModifier == _debugger["Debug"]._keyMod)
|
|
{
|
|
startDebugger();
|
|
}
|
|
// Keyboard mode
|
|
else if(_sdlKeyScanCode == _keyboard["Mode"]._scanCode && _sdlKeyModifier == _keyboard["Mode"]._keyMod)
|
|
{
|
|
int keyboardMode = _keyboardMode;
|
|
keyboardMode = (keyboardMode + 1) % (NumKeyboardModes - 1); // TODO: HwPS2 is disabled for now, (requires extra functionality in BabelFish.ino)
|
|
|
|
// Enable/disable Arduino emulated PS2 keyboard
|
|
if(keyboardMode == HwPS2) Loader::sendCommandToGiga(HW_PS2_ENABLE, true);
|
|
if(_keyboardMode == HwPS2) Loader::sendCommandToGiga(HW_PS2_DISABLE, true);
|
|
|
|
_keyboardMode = (KeyboardMode)keyboardMode;
|
|
}
|
|
// RAM/ROM mode
|
|
else if(_sdlKeyScanCode == _emulator["MemoryMode"]._scanCode && _sdlKeyModifier == _emulator["MemoryMode"]._keyMod)
|
|
{
|
|
int memoryMode = _memoryMode;
|
|
memoryMode = (_editorMode == Dasm) ? (memoryMode + 1) % (NumMemoryModes-1) : (memoryMode + 1) % NumMemoryModes;
|
|
_memoryMode = (MemoryMode)memoryMode;
|
|
|
|
// Update debugger address with PC or vPC
|
|
if(_singleStepEnabled)
|
|
{
|
|
_ntvBaseAddress = Cpu::getStateS()._PC;
|
|
_vpcBaseAddress = Cpu::getVPC();
|
|
}
|
|
}
|
|
// RAM Size
|
|
else if(_sdlKeyScanCode == _emulator["MemorySize"]._scanCode && _sdlKeyModifier == _emulator["MemorySize"]._keyMod)
|
|
{
|
|
Cpu::swapMemoryModel();
|
|
}
|
|
|
|
updateEditor();
|
|
|
|
// PS2 Keyboard emulation mode
|
|
if(gigaKeyValid && handlePs2KeyUp()) return;
|
|
|
|
// Gigatron Keyboard emulation mode
|
|
if(gigaKeyValid && handleGigaKeyUp()) return;
|
|
}
|
|
|
|
|
|
void handleGuiEvents(SDL_Event& event)
|
|
{
|
|
switch(event.type)
|
|
{
|
|
case SDL_WINDOWEVENT:
|
|
{
|
|
switch(event.window.event)
|
|
{
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
|
{
|
|
Graphics::setWidthHeight(event.window.data1, event.window.data2);
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SDL_QUIT:
|
|
{
|
|
Cpu::shutdown();
|
|
exit(0);
|
|
}
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
|
|
// Debug mode, handles it's own input and rendering
|
|
bool handleDebugger(void)
|
|
{
|
|
// Minimum vSP value and GPRINT's only checked when vPC has changed
|
|
bool vPCchanged = false;
|
|
if(Cpu::getVPC() != Cpu::getOldVPC())
|
|
{
|
|
vPCchanged = true;
|
|
Cpu::setOldVPC(Cpu::getVPC());
|
|
|
|
uint8_t vSP = Cpu::getRAM(0x001C);
|
|
uint8_t vSpMin = Assembler::getvSpMin();
|
|
if(vSP && (vSpMin == 0x00 || vSP < vSpMin))
|
|
{
|
|
Assembler::setvSpMin(vSP);
|
|
}
|
|
|
|
// GPRINTF's
|
|
Assembler::handleGprintfs();
|
|
}
|
|
|
|
// Debug
|
|
if(_singleStep)
|
|
{
|
|
static int clocks = 0;
|
|
|
|
// Native debugging
|
|
if(_memoryMode != RAM)
|
|
{
|
|
uint16_t nPC = Cpu::getStateS()._PC;
|
|
|
|
switch(_singleStepMode)
|
|
{
|
|
case RunToBrk:
|
|
{
|
|
if(_ntvBreakPoints.size() == 0)
|
|
{
|
|
resetDebugger();
|
|
}
|
|
else
|
|
{
|
|
auto it = std::find(_ntvBreakPoints.begin(), _ntvBreakPoints.end(), nPC);
|
|
if(it != _ntvBreakPoints.end())
|
|
{
|
|
beginStep(nPC);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case StepPC:
|
|
{
|
|
if(nPC != _singleStepNtv) beginStep(nPC);
|
|
}
|
|
break;
|
|
|
|
case StepWatch:
|
|
{
|
|
if(Cpu::getRAM(_singleStepAddress) != _singleStepNtv) beginStep(nPC);
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
// vCPU debugging, (this code can potentially run for every Native instruction, for efficiency we check vPC so this code only runs for each vCPU instruction)
|
|
else if(vPCchanged || clocks >= MAX_SINGLE_STEP_CLOCKS)
|
|
{
|
|
// Timeout on change of variable
|
|
if(SDL_GetTicks() - _singleStepTicks > SINGLE_STEP_STALL_CLOCKS)
|
|
{
|
|
resetDebugger();
|
|
fprintf(stderr, "Editor::handleDebugger() : Single step stall for %d milliseconds : exiting debugger.\n", SDL_GetTicks() - _singleStepTicks);
|
|
}
|
|
|
|
// Single step on breakpoint, vPC or on watch value
|
|
switch(_singleStepMode)
|
|
{
|
|
case RunToBrk:
|
|
{
|
|
if(_vpcBreakPoints.size() == 0)
|
|
{
|
|
resetDebugger();
|
|
}
|
|
else
|
|
{
|
|
auto it = std::find(_vpcBreakPoints.begin(), _vpcBreakPoints.end(), Cpu::getVPC());
|
|
if(it != _vpcBreakPoints.end())
|
|
{
|
|
beginStep(Cpu::getVPC());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case StepPC:
|
|
{
|
|
// Step whenever program counter changes or when MAX_SINGLE_STEP_CLOCKS clocks have occured, (to avoid deadlocks)
|
|
if(Cpu::getVPC() != _singleStepVpc || clocks >= MAX_SINGLE_STEP_CLOCKS) beginStep(Cpu::getVPC());
|
|
}
|
|
break;
|
|
|
|
case StepWatch:
|
|
{
|
|
if(Cpu::getRAM(_singleStepAddress) != _singleStepVpc) beginStep(Cpu::getVPC());
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
clocks = 0;
|
|
}
|
|
|
|
clocks++;
|
|
}
|
|
|
|
// Pause simulation and handle debugging keys
|
|
while(_singleStepEnabled)
|
|
{
|
|
// Update graphics but only once every 16.66667ms
|
|
static uint64_t prevFrameCounter = 0;
|
|
double frameTime = double(SDL_GetPerformanceCounter() - prevFrameCounter) / double(SDL_GetPerformanceFrequency());
|
|
|
|
Timing::setFrameUpdate(false);
|
|
if(frameTime > VSYNC_TIMING_60)
|
|
{
|
|
_onVarType = updateOnVarType();
|
|
|
|
prevFrameCounter = SDL_GetPerformanceCounter();
|
|
Timing::setFrameUpdate(true);
|
|
Graphics::refreshScreen();
|
|
Graphics::render(false);
|
|
}
|
|
|
|
SDL_Event event;
|
|
while(SDL_PollEvent(&event))
|
|
{
|
|
_sdlKeyScanCode = event.key.keysym.sym;
|
|
_sdlKeyModifier = event.key.keysym.mod & (KMOD_LCTRL | KMOD_LALT);
|
|
_mouseState._state = SDL_GetMouseState(&_mouseState._x, &_mouseState._y);
|
|
|
|
handleGuiEvents(event);
|
|
|
|
switch(event.type)
|
|
{
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
{
|
|
if(_pageUpButton && event.button.button == SDL_BUTTON_LEFT) handlePageUp(HEX_CHARS_Y);
|
|
else if(_pageDnButton && event.button.button == SDL_BUTTON_LEFT) handlePageDown(HEX_CHARS_Y);
|
|
else if(_memoryMode == RAM && _delAllButton && event.button.button == SDL_BUTTON_LEFT) _vpcBreakPoints.clear();
|
|
else if(_memoryMode != RAM && _delAllButton && event.button.button == SDL_BUTTON_LEFT) _ntvBreakPoints.clear();
|
|
else if(event.button.button == SDL_BUTTON_LEFT) handleMouseLeftClick();
|
|
}
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
{
|
|
handleMouseWheel(event);
|
|
}
|
|
break;
|
|
|
|
case SDL_KEYUP:
|
|
{
|
|
// Leave debug mode
|
|
if((_sdlKeyScanCode == _debugger["Debug"]._scanCode && _sdlKeyModifier == _debugger["Debug"]._keyMod))
|
|
{
|
|
resetDebugger();
|
|
}
|
|
else
|
|
{
|
|
handleKeyUp(!_hexEdit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SDL_KEYDOWN:
|
|
{
|
|
// Run to breakpoint, (vCPU or native)
|
|
if(_sdlKeyScanCode == _debugger["RunToBrk"]._scanCode && _sdlKeyModifier == _debugger["RunToBrk"]._keyMod)
|
|
{
|
|
runToBreakpoint();
|
|
}
|
|
// Step on watch value, (vCPU or native)
|
|
else if(_sdlKeyScanCode == _debugger["StepWatch"]._scanCode && _sdlKeyModifier == _debugger["StepWatch"]._keyMod)
|
|
{
|
|
singleStepWatch();
|
|
}
|
|
// Step on vPC or on PC, (vCPU or native)
|
|
else if(_sdlKeyScanCode == _debugger["StepPC"]._scanCode && _sdlKeyModifier == _debugger["StepPC"]._keyMod)
|
|
{
|
|
singleStepPc();
|
|
}
|
|
else
|
|
{
|
|
handleKeyDown(!_hexEdit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return _singleStep;
|
|
}
|
|
|
|
void handlePS2key(SDL_Event& event, bool gtKeyValid)
|
|
{
|
|
if(!gtKeyValid) return;
|
|
|
|
switch(_keyboardMode)
|
|
{
|
|
case PS2: Cpu::setIN(event.text.text[0]); break;
|
|
case HwPS2: Loader::sendCommandToGiga(event.text.text[0], true); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void handleInput(void)
|
|
{
|
|
_onVarType = updateOnVarType();
|
|
|
|
SDL_Event event;
|
|
while(SDL_PollEvent(&event))
|
|
{
|
|
_sdlKeyScanCode = event.key.keysym.sym;
|
|
_sdlKeyModifier = event.key.keysym.mod & (KMOD_LCTRL | KMOD_LALT);
|
|
_mouseState._state = SDL_GetMouseState(&_mouseState._x, &_mouseState._y);
|
|
|
|
handleGuiEvents(event);
|
|
|
|
switch(event.type)
|
|
{
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
{
|
|
if(_pageUpButton && event.button.button == SDL_BUTTON_LEFT) handlePageUp(HEX_CHARS_Y);
|
|
else if(_pageDnButton && event.button.button == SDL_BUTTON_LEFT) handlePageDown(HEX_CHARS_Y);
|
|
else if(_memoryMode == RAM && _delAllButton && event.button.button == SDL_BUTTON_LEFT) _vpcBreakPoints.clear();
|
|
else if(_memoryMode != RAM && _delAllButton && event.button.button == SDL_BUTTON_LEFT) _ntvBreakPoints.clear();
|
|
else if(event.button.button == SDL_BUTTON_LEFT) handleMouseLeftClick();
|
|
else if(event.button.button == SDL_BUTTON_RIGHT) handleMouseRightClick();
|
|
}
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL: handleMouseWheel(event); break;
|
|
case SDL_TEXTINPUT: handlePS2key(event, !_hexEdit); break;
|
|
case SDL_KEYDOWN: handleKeyDown(!_hexEdit); break;
|
|
case SDL_KEYUP: handleKeyUp(!_hexEdit); break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
if(_keyboardMode == PS2 && (!_hexEdit))
|
|
{
|
|
int numKeys;
|
|
const Uint8 *keyboardState = SDL_GetKeyboardState(&numKeys);
|
|
bool anyKeyPressed = false;
|
|
static bool anyKeyPressedPrev = anyKeyPressed;
|
|
for(int i=0; i<numKeys; i++)
|
|
{
|
|
if(keyboardState[i])
|
|
{
|
|
anyKeyPressed = true;
|
|
break;
|
|
}
|
|
}
|
|
if(anyKeyPressedPrev && !anyKeyPressed) Cpu::setIN(0xFF);
|
|
anyKeyPressedPrev = anyKeyPressed;
|
|
}
|
|
|
|
// Updates current game's high score once per second, (assuming handleInput is called in vertical blank)
|
|
Loader::updateHighScore();
|
|
}
|
|
}
|