gigatron/rom/Contrib/at67/editor.cpp
2025-01-28 19:17:01 +03:00

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();
}
}