mirror of
https://github.com/cmosher01/Epple-II.git
synced 2024-06-09 08:29:32 +00:00
new config hadling, first pass; refactor apple core loop; search resources for imported rom files
This commit is contained in:
parent
e74c631d44
commit
c2bd2dcee3
|
@ -54,6 +54,11 @@ wxIMPLEMENT_APP_NO_MAIN(E2wxApp);
|
||||||
#define PROJECT_VERSION 0.0.1
|
#define PROJECT_VERSION 0.0.1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
std::promise<void> E2wxApp::barrier_to_init;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
E2wxApp::E2wxApp() : id("nu.mine.mosher.epple2"), version(wxSTRINGIZE_T(PROJECT_VERSION)) {
|
E2wxApp::E2wxApp() : id("nu.mine.mosher.epple2"), version(wxSTRINGIZE_T(PROJECT_VERSION)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +156,7 @@ bool E2wxApp::OnInit() {
|
||||||
frame->Show();
|
frame->Show();
|
||||||
|
|
||||||
|
|
||||||
|
barrier_to_init.set_value();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
class E2wxApp : public wxApp {
|
class E2wxApp : public wxApp {
|
||||||
const std::string id;
|
const std::string id;
|
||||||
|
@ -41,6 +42,8 @@ class E2wxApp : public wxApp {
|
||||||
void InitBoostLog();
|
void InitBoostLog();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static std::promise<void> barrier_to_init;
|
||||||
|
|
||||||
E2wxApp();
|
E2wxApp();
|
||||||
virtual ~E2wxApp();
|
virtual ~E2wxApp();
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ void PreferencesDialog::OnInit() {
|
||||||
wxConfigBase *appconf = wxConfigBase::Get();
|
wxConfigBase *appconf = wxConfigBase::Get();
|
||||||
if (!appconf->Read(wxT("/ActivePreferences/name"), &this->active)) {
|
if (!appconf->Read(wxT("/ActivePreferences/name"), &this->active)) {
|
||||||
// TODO what to do when no config?
|
// TODO what to do when no config?
|
||||||
this->active = ".template";
|
this->active = "epple2";
|
||||||
appconf->Write(wxT("/ActivePreferences/name"), this->active);
|
appconf->Write(wxT("/ActivePreferences/name"), this->active);
|
||||||
appconf->Flush();
|
appconf->Flush();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
*/
|
*/
|
||||||
#include "configep2.h"
|
#include "configep2.h"
|
||||||
|
|
||||||
|
#include "E2wxApp.h"
|
||||||
|
|
||||||
#include "apple2.h"
|
#include "apple2.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "memoryrandomaccess.h"
|
#include "memoryrandomaccess.h"
|
||||||
|
@ -31,6 +33,9 @@
|
||||||
#include "cassetteout.h"
|
#include "cassetteout.h"
|
||||||
#include "tinyfiledialogs.h"
|
#include "tinyfiledialogs.h"
|
||||||
|
|
||||||
|
#include <wx/config.h>
|
||||||
|
#include <wx/string.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -105,11 +110,46 @@ void Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revis
|
||||||
pConfig = new std::ifstream(path.c_str());
|
pConfig = new std::ifstream(path.c_str());
|
||||||
if (!pConfig->is_open())
|
if (!pConfig->is_open())
|
||||||
{
|
{
|
||||||
|
// TODO use filename only and look in standard resources
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Cannot open config file " << this->file_path.c_str();
|
ss << "Cannot open config file " << this->file_path.c_str();
|
||||||
throw std::runtime_error(ss.str());
|
throw std::runtime_error(ss.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
{
|
||||||
|
// TODO config file location, how to be backwardly compatible?
|
||||||
|
wxString user_config;
|
||||||
|
if (!wxConfigBase::Get()->Read(wxT("/ActivePreferences/name"), &user_config)) {
|
||||||
|
// TODO what to do when no config?
|
||||||
|
user_config = wxT("epple2");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path f = wxGetApp().GetConfigDir();
|
||||||
|
f /= user_config.t_str();
|
||||||
|
f += ".conf";
|
||||||
|
path = f.string();
|
||||||
|
std::cout << "looking for config file: " << path << std::endl;
|
||||||
|
pConfig = new std::ifstream(path.c_str());
|
||||||
|
if (!pConfig->is_open()) {
|
||||||
|
f = wxGetApp().GetResDir();
|
||||||
|
f /= user_config.t_str();
|
||||||
|
f += ".conf";
|
||||||
|
path = f.string();
|
||||||
|
std::cout << "looking for config file: " << path << std::endl;
|
||||||
|
pConfig = new std::ifstream(path.c_str());
|
||||||
|
if (!pConfig->is_open()) {
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
{
|
{
|
||||||
std::cout << "standard config file location: " ETCDIR "/epple2/epple2.conf" << std::endl;
|
std::cout << "standard config file location: " ETCDIR "/epple2/epple2.conf" << std::endl;
|
||||||
|
@ -304,17 +344,23 @@ void Config::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memo
|
||||||
std::string file;
|
std::string file;
|
||||||
std::getline(tok,file);
|
std::getline(tok,file);
|
||||||
trim(file);
|
trim(file);
|
||||||
std::ifstream memfile(file.c_str(),std::ios::binary);
|
std::ifstream *memfile = new std::ifstream(file.c_str(),std::ios::binary);
|
||||||
if (!memfile.is_open())
|
if (!memfile->is_open())
|
||||||
{
|
{
|
||||||
throw ConfigException("cannot open file "+file);
|
std::filesystem::path f = wxGetApp().GetResDir();
|
||||||
|
f /= file;
|
||||||
|
memfile = new std::ifstream(f,std::ios::binary);
|
||||||
|
if (!memfile->is_open())
|
||||||
|
{
|
||||||
|
throw ConfigException("cannot open file "+file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slot < 0) // motherboard
|
if (slot < 0) // motherboard
|
||||||
{
|
{
|
||||||
if (romtype == "rom")
|
if (romtype == "rom")
|
||||||
{
|
{
|
||||||
rom.load(base,memfile);
|
rom.load(base,*memfile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -329,15 +375,15 @@ void Config::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memo
|
||||||
}
|
}
|
||||||
Card* card = slts.get(slot);
|
Card* card = slts.get(slot);
|
||||||
if (romtype == "rom")
|
if (romtype == "rom")
|
||||||
card->loadRom(base,memfile);
|
card->loadRom(base,*memfile);
|
||||||
else if (romtype == "rom7")
|
else if (romtype == "rom7")
|
||||||
card->loadSeventhRom(base,memfile);
|
card->loadSeventhRom(base,*memfile);
|
||||||
else if (romtype == "rombank")
|
else if (romtype == "rombank")
|
||||||
card->loadBankRom(base,memfile);
|
card->loadBankRom(base,*memfile);
|
||||||
else
|
else
|
||||||
throw ConfigException("error at \""+romtype+"\"; expected rom, rom7, or rombank");
|
throw ConfigException("error at \""+romtype+"\"; expected rom, rom7, or rombank");
|
||||||
}
|
}
|
||||||
memfile.close();
|
memfile->close();
|
||||||
}
|
}
|
||||||
else if (cmd == "load" || cmd == "save" || cmd == "unload")
|
else if (cmd == "load" || cmd == "save" || cmd == "unload")
|
||||||
{
|
{
|
||||||
|
@ -375,6 +421,7 @@ void Config::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fn_optional.length() > 0) {
|
if (fn_optional.length() > 0) {
|
||||||
|
// TODO check if file exists, if not then check resources
|
||||||
loadDisk(slts,slot,drive,fn_optional);
|
loadDisk(slts,slot,drive,fn_optional);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
147
src/emulator.cpp
147
src/emulator.cpp
|
@ -95,81 +95,101 @@ void Emulator::init() {
|
||||||
static const int CYCLES_PER_REPT(E2Const::AVG_CPU_HZ / 10);
|
static const int CYCLES_PER_REPT(E2Const::AVG_CPU_HZ / 10);
|
||||||
|
|
||||||
|
|
||||||
// The core of this Apple
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// If the Apple ][ keyboard repeat is on (the REPT key is
|
||||||
|
// down)...
|
||||||
|
void Emulator::handleRepeatKey() {
|
||||||
|
if (this->repeat) {
|
||||||
|
// Count our way down to when the timer for the REPT key
|
||||||
|
// fires off: 10Hz in terms of how many CPU cycles have gone
|
||||||
|
// by
|
||||||
|
--this->rept;
|
||||||
|
// If it's time for the REPT key timer to fire (at long
|
||||||
|
// last)...
|
||||||
|
if (this->rept <= 0) {
|
||||||
|
// ...reload the timer for the next firing 1/10 second from
|
||||||
|
// now ( *reset* the timer )
|
||||||
|
this->rept = CYCLES_PER_REPT;
|
||||||
|
// If any other keys are actually being held down...
|
||||||
|
if (this->keysDown > 0) {
|
||||||
|
// ...REPEAT the most recent one that was pressed
|
||||||
|
this->keypresses.push(this->lastKeyDown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emulator::handleAnyPendingEvents() {
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
switch (event.type) {
|
||||||
|
case SDL_QUIT:
|
||||||
|
// If SDL is going away...
|
||||||
|
quitIfSafe();
|
||||||
|
break;
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
// If we're collecting a command line for changing any
|
||||||
|
// of the configurables of the emulator...
|
||||||
|
if (this->command)
|
||||||
|
cmdKey(event.key);
|
||||||
|
else
|
||||||
|
// ...else we're collecting keypresses for the keyboard
|
||||||
|
// emulation (and thus the Apple ][ emulation itself)
|
||||||
|
dispatchKeypress(event.key);
|
||||||
|
break;
|
||||||
|
case SDL_KEYUP:
|
||||||
|
// If we're collecting a command line for changing any
|
||||||
|
// of the configurables of the emulator...
|
||||||
|
if (this->command) {
|
||||||
|
if (this->pendingCommandExit) {
|
||||||
|
this->command = false;
|
||||||
|
this->pendingCommandExit = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ...else we're collecting keypresses for the keyboard
|
||||||
|
// emulation (and thus the Apple ][ emulation itself)
|
||||||
|
dispatchKeyUp(event.key);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// The core of this Apple
|
||||||
int Emulator::run() {
|
int Emulator::run() {
|
||||||
int skip = CHECK_EVERY_CYCLE;
|
int skip = 0;
|
||||||
Uint32 prev_ms = SDL_GetTicks();
|
Uint32 prev_ms = SDL_GetTicks();
|
||||||
|
|
||||||
// While the user still wants to run this emulation...
|
// While the user still wants to run this emulation...
|
||||||
while (!this->quit) {
|
while (!this->quit) {
|
||||||
// (Obligatory protection against NULL object pointer)
|
// (Obligatory protection against NULL object pointer)
|
||||||
if (this->timable) {
|
if (this->timable) {
|
||||||
this->timable->tick();
|
this->timable->tick();
|
||||||
// If the Apple ][ keyboard repeat is on (the REPT key is
|
handleRepeatKey();
|
||||||
// down)...
|
|
||||||
if (this->repeat) {
|
|
||||||
// Count our way down to when the timer for the REPT key
|
|
||||||
// fires off: 10Hz in terms of how many CPU cycles have gone
|
|
||||||
// by
|
|
||||||
--this->rept;
|
|
||||||
// If it's time for the REPT key timer to fire (at long
|
|
||||||
// last)...
|
|
||||||
if (this->rept <= 0) {
|
|
||||||
// ...reload the timer for the next firing 1/10 second from
|
|
||||||
// now ( *reset* the timer )
|
|
||||||
this->rept = CYCLES_PER_REPT;
|
|
||||||
// If any other keys are actually being held down...
|
|
||||||
if (this->keysDown > 0) {
|
|
||||||
// ...REPEAT the most recent one that was pressed
|
|
||||||
this->keypresses.push(this->lastKeyDown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// People who have too many press releases should be referred to as
|
// People who have too many press releases should be referred to as
|
||||||
// keyboards
|
// keyboards
|
||||||
|
|
||||||
--skip;
|
if (CHECK_EVERY_CYCLE <= ++skip) {
|
||||||
// If skip has been decremented to zero...
|
|
||||||
if (!skip) {
|
|
||||||
// ...then it's time to drain away any piled-up user interaction
|
// ...then it's time to drain away any piled-up user interaction
|
||||||
// events that SDL has stored up for us
|
// events that SDL has stored up for us
|
||||||
// Reload the skip quantity
|
// Reload the skip quantity
|
||||||
skip = CHECK_EVERY_CYCLE;
|
skip = 0;
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
handleAnyPendingEvents();
|
||||||
switch (event.type) {
|
|
||||||
// If SDL is going away...
|
|
||||||
case SDL_QUIT:
|
|
||||||
quitIfSafe();
|
|
||||||
break;
|
|
||||||
case SDL_KEYDOWN:
|
|
||||||
// If we're collecting a command line for changing any
|
|
||||||
// of the configurables of the emulator...
|
|
||||||
if (this->command)
|
|
||||||
cmdKey(event.key);
|
|
||||||
else
|
|
||||||
// ...else we're collecting keypresses for the keyboard
|
|
||||||
// emulation (and thus the Apple ][ emulation itself)
|
|
||||||
dispatchKeypress(event.key);
|
|
||||||
break;
|
|
||||||
case SDL_KEYUP:
|
|
||||||
// If we're collecting a command line for changing any
|
|
||||||
// of the configurables of the emulator...
|
|
||||||
if (this->command) {
|
|
||||||
if (this->pendingCommandExit) {
|
|
||||||
this->command = false;
|
|
||||||
this->pendingCommandExit = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// ...else we're collecting keypresses for the keyboard
|
|
||||||
// emulation (and thus the Apple ][ emulation itself)
|
|
||||||
dispatchKeyUp(event.key);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we're trying to run as slow as a real Apple ][...
|
// If we're trying to run as slow as a real Apple ][...
|
||||||
if (!this->fhyper.isHyper()) {
|
if (!this->fhyper.isHyper()) {
|
||||||
const int delta_ms = EXPECTED_MS - (SDL_GetTicks() - prev_ms);
|
const int delta_ms = EXPECTED_MS - (SDL_GetTicks() - prev_ms);
|
||||||
|
@ -178,15 +198,23 @@ int Emulator::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display the current estimate of the emulator's actual speed
|
// Display the current estimate of the emulator's actual speed
|
||||||
// performance
|
// performance
|
||||||
this->screenImage.displayHz(CHECK_CYCLES_K / (SDL_GetTicks() - prev_ms));
|
this->screenImage.displayHz(CHECK_CYCLES_K / (SDL_GetTicks() - prev_ms));
|
||||||
|
|
||||||
prev_ms = SDL_GetTicks();
|
prev_ms = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Emulator::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent) {
|
void Emulator::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent) {
|
||||||
SDL_Keycode sym = keyEvent.keysym.sym;
|
SDL_Keycode sym = keyEvent.keysym.sym;
|
||||||
SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod;
|
SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod;
|
||||||
|
@ -429,7 +457,6 @@ void Emulator::cmdKey(const SDL_KeyboardEvent& keyEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process a command line typed at the bottom of the emulator window
|
// Process a command line typed at the bottom of the emulator window
|
||||||
|
|
||||||
void Emulator::processCommand() {
|
void Emulator::processCommand() {
|
||||||
this->screenImage.exitCommandMode();
|
this->screenImage.exitCommandMode();
|
||||||
this->screenImage.drawPower(this->timable == &this->apple2);
|
this->screenImage.drawPower(this->timable == &this->apple2);
|
||||||
|
|
|
@ -62,6 +62,9 @@ class Emulator
|
||||||
void processCommand();
|
void processCommand();
|
||||||
bool isSafeToQuit();
|
bool isSafeToQuit();
|
||||||
|
|
||||||
|
void handleRepeatKey();
|
||||||
|
void handleAnyPendingEvents();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Emulator();
|
Emulator();
|
||||||
virtual ~Emulator();
|
virtual ~Emulator();
|
||||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -26,6 +26,7 @@
|
||||||
#include <wx/app.h>
|
#include <wx/app.h>
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <future>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
@ -48,19 +49,26 @@ static std::string parse_args(int argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int fake_argc(0);
|
static int fake_argc(1);
|
||||||
static char fake_prog[] = "epple2";
|
static char fake_prog[] = "epple2";
|
||||||
static char *fake_argv[] { fake_prog };
|
static char *fake_argv[] { fake_prog };
|
||||||
|
|
||||||
static int runWx() {
|
static int runWx() {
|
||||||
|
std::cout << "starting wx..." << std::endl;
|
||||||
return wxEntry(fake_argc, fake_argv);
|
return wxEntry(fake_argc, fake_argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int runSdl(const std::string config_file) {
|
static int runSdl(const std::string config_file) {
|
||||||
GUI gui;
|
GUI gui;
|
||||||
|
|
||||||
|
std::future<void> barrier_to_app_init = E2wxApp::barrier_to_init.get_future();
|
||||||
|
|
||||||
std::thread thread_wx(runWx);
|
std::thread thread_wx(runWx);
|
||||||
|
|
||||||
|
std::cout << "wait for wx init" << std::endl;
|
||||||
|
barrier_to_app_init.wait();
|
||||||
|
std::cout << "wx init finished" << std::endl;
|
||||||
|
|
||||||
std::unique_ptr<Emulator> emu(new Emulator());
|
std::unique_ptr<Emulator> emu(new Emulator());
|
||||||
|
|
||||||
Config cfg(config_file);
|
Config cfg(config_file);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user