diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4fd1472..ead972c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,7 @@ diskcontroller.cpp
drive.cpp
drivemotor.cpp
e2config.cpp
+e2command.cpp
e2filesystem.cpp
e2string.cpp
E2wxApp.cpp
diff --git a/src/card.cpp b/src/card.cpp
index 7ad3119..aad3389 100644
--- a/src/card.cpp
+++ b/src/card.cpp
@@ -14,96 +14,89 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-*/
+ */
#include "card.h"
-#include "e2config.h"
-Card::Card():
- rom(0x0100),
- seventhRom(0x0800)
-{
+
+
+Card::Card() : rom(0x0100), seventhRom(0x0800) {
+}
+
+Card::~Card() {
}
-Card::~Card()
-{
+
+std::string Card::getName() {
+ return "";
}
-void Card::reset()
-{
+
+void Card::tick() {
}
-void Card::tick()
-{
+void Card::reset() {
}
-
-unsigned char Card::io(const unsigned short /*address*/, const unsigned char data, const bool /*writing*/)
-{
+unsigned char Card::io(const unsigned short address, const unsigned char data, const bool writing) {
return data;
}
-
-
-unsigned char Card::readRom(const unsigned short address, const unsigned char data)
-{
+unsigned char Card::readRom(const unsigned short address, const unsigned char data) {
this->activeSeventhRom = true;
return this->rom.read(address, data);
}
-void Card::readSeventhRom(const unsigned short address, unsigned char* const pb)
-{
- if (address == 0x7FF)
- {
+
+
+bool Card::hasSeventhRom() {
+ return false;
+}
+
+void Card::readSeventhRom(const unsigned short address, unsigned char* const pb) {
+ if (address == 0x7FF) {
this->activeSeventhRom = false;
- }
- else if (this->activeSeventhRom && hasSeventhRom())
- {
+ } else if (this->activeSeventhRom && hasSeventhRom()) {
*pb = this->seventhRom.read(address, *pb);
}
}
-void Card::loadRom(const unsigned short base, std::istream& in)
-{
- this->rom.load(base,in);
+void Card::loadRom(const unsigned short base, std::istream& in) {
+ this->rom.load(base, in);
}
-void Card::loadSeventhRom(const unsigned short base, std::istream& in)
-{
- this->seventhRom.load(base,in);
+void Card::loadSeventhRom(const unsigned short base, std::istream& in) {
+ this->seventhRom.load(base, in);
}
-
-
-bool Card::inhibitMotherboardRom()
-{
+bool Card::inhibitMotherboardRom() {
return false;
}
+void Card::ioBankRom(const unsigned short addr, unsigned char* const pb, const bool write) {
+}
-
-void Card::ioBankRom(const unsigned short /*addr*/, unsigned char* const /*pb*/, const bool /*write*/)
-{
+void Card::loadBankRom(const unsigned short base, std::istream& in) {
+ // TODO? maybe just do nothing
+ // throw ConfigException("This card has no $D000 ROM");
}
-void Card::loadBankRom(const unsigned short /*base*/, std::istream& /*in*/)
-{
- throw ConfigException("This card has no $D000 ROM");
-}
-
-std::string Card::getName()
-{
- return "";
-}
-
-bool Card::isDirty()
-{
+bool Card::isMediaDirty() {
return false;
}
-void Card::save(int unit)
-{
+bool Card::hasMedia() {
+ return false;
+}
+
+void Card::loadMedia(int unit, const std::filesystem::path &media) {
+}
+
+void Card::unloadMedia(int unit) {
+}
+
+void Card::saveMedia(int unit) {
}
diff --git a/src/card.h b/src/card.h
index 004602a..b0bf349 100644
--- a/src/card.h
+++ b/src/card.h
@@ -14,19 +14,19 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-*/
+ */
#ifndef CARD_H
#define CARD_H
#include "memory.h"
+#include
#include
#include
-class Card
-{
-private:
+class Card {
bool activeSeventhRom;
+
protected:
Memory rom;
Memory seventhRom;
@@ -34,20 +34,27 @@ protected:
public:
Card();
virtual ~Card();
- virtual void tick();
- virtual void reset();
+
+ virtual std::string getName();
+
+ virtual void tick();
+ virtual void reset();
virtual unsigned char io(const unsigned short address, const unsigned char data, const bool writing);
virtual unsigned char readRom(const unsigned short address, const unsigned char data);
- virtual bool hasSeventhRom() { return false; }
+
+ virtual bool hasSeventhRom();
virtual void readSeventhRom(const unsigned short address, unsigned char* const pb);
virtual void loadRom(const unsigned short base, std::istream& in);
virtual void loadSeventhRom(const unsigned short base, std::istream& in);
virtual bool inhibitMotherboardRom();
virtual void ioBankRom(const unsigned short addr, unsigned char* const pb, const bool write);
virtual void loadBankRom(const unsigned short base, std::istream& in);
- virtual bool isDirty();
- virtual void save(int unit);
- virtual std::string getName();
+ virtual bool isMediaDirty();
+
+ virtual bool hasMedia();
+ virtual void loadMedia(int unit, const std::filesystem::path &media);
+ virtual void unloadMedia(int unit);
+ virtual void saveMedia(int unit);
};
#endif
diff --git a/src/diskcontroller.h b/src/diskcontroller.h
index 3963059..4fee374 100644
--- a/src/diskcontroller.h
+++ b/src/diskcontroller.h
@@ -21,6 +21,7 @@
#include "wozfile.h"
#include "lss.h"
#include "screenimage.h"
+#include
#include
#include
#include
@@ -94,21 +95,25 @@ public:
this->gui.setCurrentDrive(this->slot,getCurrentDriveNumber(),getTrack(),false);
}
- void loadDisk(unsigned char drive, const std::string& fnib) {
- if (!this->getDrive(drive).loadDisk(fnib)) {
+ virtual bool hasMedia() override {
+ return true;
+ }
+
+ virtual void loadMedia(int unit, const std::filesystem::path &media) override {
+ if (!this->getDrive(unit).loadDisk(media)) {
return;
}
- this->gui.setDiskFile(this->slot,drive,fnib);
+ this->gui.setDiskFile(this->slot,unit,media);
this->gui.setDirty(this->slot,getCurrentDriveNumber(),false);
}
- void unloadDisk(unsigned char drive) {
- this->getDrive(drive).unloadDisk();
- this->gui.setDiskFile(this->slot,drive,"");
+ virtual void unloadMedia(int unit) override {
+ this->getDrive(unit).unloadDisk();
+ this->gui.setDiskFile(this->slot,unit,std::filesystem::path{});
this->gui.setDirty(this->slot,getCurrentDriveNumber(),false);
}
- void save(int drive) {
+ virtual void saveMedia(int drive) override {
this->getDrive(drive).saveDisk();
this->gui.setDirty(this->slot,getCurrentDriveNumber(),false);
}
@@ -137,7 +142,7 @@ public:
return this->currentDrive->isWriteProtected();
}
- bool isDirty() {
+ bool isMediaDirty() {
return isModified() || isModifiedOther();
}
diff --git a/src/e2command.cpp b/src/e2command.cpp
new file mode 100644
index 0000000..e094ee4
--- /dev/null
+++ b/src/e2command.cpp
@@ -0,0 +1,406 @@
+/*
+ epple2
+ Copyright (C) 2008 by Christopher A. Mosher
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY, without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * File: e2command.cpp
+ * Author: user
+ *
+ * Created on December 11, 2022, 8:21 PM
+ */
+
+
+
+#include "e2command.h"
+#include "E2wxApp.h"
+#include "apple2.h"
+#include "memoryrandomaccess.h"
+#include "memorychip.h"
+#include "cassette.h"
+#include "cassettein.h"
+#include "cassetteout.h"
+#include "card.h"
+#include "slots.h"
+#include "diskcontroller.h"
+#include "languagecard.h"
+#include "firmwarecard.h"
+#include "standardout.h"
+#include "standardin.h"
+#include "clockcard.h"
+#include "screenimage.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+
+
+#define K 1024u
+
+static std::uint16_t memory_block_size(const std::string &block_size) {
+ if (block_size == "4K") {
+ return 4u*K;
+ }
+ if (block_size == "16K") {
+ return 16u*K;
+ }
+ throw ConfigException("invalid RAM strapping block size (must be 4K or 16K)");
+}
+
+static void trim(std::string& s) {
+ wxString w{s};
+ s = w.Trim(false).Trim(true);
+}
+
+static std::string filter_row(const std::string &row) {
+ if (row.length() != 1) {
+ return "";
+ }
+ return std::string(1, static_cast (std::toupper(row[0])));
+}
+
+
+
+E2Command::E2Command() {
+}
+
+E2Command::~E2Command() {
+}
+
+
+
+void E2Command::parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2) {
+ try {
+ tryParseLine(line, ram, rom, slts, revision, gui, cassetteIn, cassetteOut, apple2);
+ } catch (const ConfigException& err) {
+ // TODO fix error handling
+ std::cerr << err.msg.c_str() << std::endl;
+ }
+}
+
+
+
+void E2Command::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2) {
+ std::istringstream tok(line);
+
+ std::string cmd;
+ tok >> cmd;
+ if (cmd == "slot") {
+ int slot;
+ std::string sCardType;
+ tok >> slot >> sCardType;
+
+ double random_ones_rate(0.3); // WOZ spec v2.0: 30%
+ if (sCardType=="disk" || sCardType=="disk13") {
+ tok >> random_ones_rate;
+ }
+
+ insertCard(sCardType, slot, slts, gui, random_ones_rate);
+ } else if (cmd == "motherboard") {
+ std::string op;
+ tok >> op;
+ if (op == "ram") {
+ /* ram ROW BIT0 [BIT1 [... [BIT7]]]
+ * ram e -
+ * ram d 4096 MK4096 4K
+ * ram c 16K 4116 MK4116 MM5290 16K 16K 16K 16K
+ */
+ std::string row;
+ tok >> row;
+ row = filter_row(row);
+ if (row != "C" && row != "D" && row != "E") {
+ throw ConfigException("expected row to be C, D, or E");
+ }
+
+ std::string chip_model;
+ tok >> chip_model;
+ for (std::uint_fast8_t bit = 0u; bit < 8u; ++bit) {
+ ram.insert_chip(row, MemoryChip::instance(chip_model), bit);
+
+ std::string chip_model_optional;
+ tok >> chip_model_optional;
+ if (chip_model_optional.length()) {
+ chip_model = chip_model_optional;
+ }
+ }
+ } else if (op == "strap") {
+ /* strap ROM K start-addr
+ * strap e 4K 5000
+ * strap d 4K 4000
+ * strap c 16K 0000
+ */
+ std::string row;
+ tok >> row;
+ row = filter_row(row);
+ if (row != "C" && row != "D" && row != "E") {
+ throw ConfigException("expected row to be C, D, or E");
+ }
+
+ std::string block_size;
+ tok >> block_size;
+ std::uint16_t siz = memory_block_size(block_size);
+ unsigned short base(0);
+ tok >> std::hex >> base;
+ // TODO validate siz/base combination
+ ram.strap_to(row, base, siz);
+ } else {
+ throw ConfigException("error at \"motherboard\"; expected \"ram\" or \"strap\"");
+ }
+ } else if (cmd == "import") {
+ std::string sm;
+ tok >> sm;
+
+ int slot(-1);
+ if (sm == "slot") {
+ tok >> slot;
+ } else if (sm != "motherboard") {
+ throw ConfigException("error at \"" + sm + "\"; expected \"slot #\" or \"motherboard\"");
+ }
+
+ std::string romtype;
+ tok >> romtype;
+
+ unsigned short base(0);
+ tok >> std::hex >> base;
+
+ std::string file;
+ std::getline(tok, file);
+ trim(file);
+ std::ifstream *memfile = new std::ifstream(file.c_str(), std::ios::binary);
+ if (!memfile->is_open()) {
+ 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 (romtype == "rom") {
+ rom.load(base, *memfile);
+ } else {
+ throw ConfigException("error at \"" + romtype + "\"; expected rom or ram");
+ }
+ } else {
+ if (8 <= slot) {
+ throw ConfigException("invalid slot number");
+ }
+ Card* card = slts.get(slot);
+ if (romtype == "rom")
+ card->loadRom(base, *memfile);
+ else if (romtype == "rom7")
+ card->loadSeventhRom(base, *memfile);
+ else if (romtype == "rombank")
+ card->loadBankRom(base, *memfile);
+ else
+ throw ConfigException("error at \"" + romtype + "\"; expected rom, rom7, or rombank");
+ }
+ memfile->close();
+ } else if (cmd == "load" || cmd == "save" || cmd == "unload") {
+ std::string slotk;
+ tok >> slotk;
+ if (slotk != "slot") {
+ throw ConfigException("error at \"" + slotk + "\"; expected \"slot\"");
+ }
+
+ int slot(-1);
+ tok >> slot;
+
+ std::string drivek;
+ tok >> drivek;
+ if (drivek != "drive") {
+ throw ConfigException("error at \"" + drivek + "\"; expected \"drive\"");
+ }
+
+ int drive(-1);
+ tok >> drive;
+
+ if (cmd == "load") {
+ std::string fn_optional;
+ std::getline(tok, fn_optional);
+ trim(fn_optional);
+ if (fn_optional.length() == 0) {
+ gui.exitFullScreen();
+ wxFileDialog dlg{nullptr, "Load floppy", "", "", "WOZ 2.0 disk images (*.woz)|*.woz", wxFD_OPEN|wxFD_FILE_MUST_EXIST};
+ if (dlg.ShowModal() == wxID_OK) {
+ fn_optional = dlg.GetPath().c_str();
+ }
+ }
+ if (fn_optional.length() > 0) {
+ loadDisk(slts, slot, drive, fn_optional);
+ }
+ } else if (cmd == "unload") {
+ unloadDisk(slts, slot, drive);
+ } else if (cmd == "save") {
+ saveDisk(slts, slot, drive);
+ }
+ } else if (cmd == "revision") {
+ tok >> std::hex >> revision;
+ } else if (cmd == "cassette") {
+ std::string cas;
+ tok >> cas;
+
+ if (cas == "rewind") {
+ cassetteIn.rewind();
+ } else if (cas == "tone") {
+ cassetteIn.tone();
+ } else if (cas == "blank") {
+ std::string fcas;
+ std::getline(tok, fcas);
+ trim(fcas);
+ if (!fcas.empty()) {
+ cassetteOut.blank(fcas);
+ }
+ } else if (cas == "load") {
+ std::string fn_optional;
+ std::getline(tok, fn_optional);
+ trim(fn_optional);
+ if (fn_optional.length() == 0) {
+ gui.exitFullScreen();
+ wxFileDialog dlg{nullptr, "Load cassette (audio)", "", "", "WAVE cassette images (*.wav)|*.wav", wxFD_OPEN|wxFD_FILE_MUST_EXIST};
+ if (dlg.ShowModal() == wxID_OK) {
+ fn_optional = dlg.GetPath().c_str();
+ }
+ }
+ if (fn_optional.length() > 0) {
+ cassetteIn.load(fn_optional);
+ }
+ } else if (cas == "eject") {
+ std::string eject;
+ tok >> eject;
+ if (eject == "in") {
+ cassetteIn.eject();
+ } else if (eject == "out") {
+ cassetteOut.eject();
+ } else {
+ throw ConfigException("error: unknown cassette to eject: " + eject);
+ }
+ } else if (cas == "save") {
+ cassetteOut.save();
+ } else {
+ throw ConfigException("error: unknown cassette command: " + cas);
+ }
+ } else if (cmd == "cpu") {
+ std::string cpu;
+ tok >> cpu;
+ if (apple2 != NULL) {
+ if (cpu == "epple2") {
+ apple2->useEpple2Cpu();
+ } else if (cpu == "visual6502") {
+ apple2->useVisual6502Cpu();
+ } else {
+ throw ConfigException("invalid value for cpu command: " + cpu);
+ }
+ }
+ } else {
+ throw ConfigException("Invalid command: " + cmd);
+ }
+
+ if (apple2 != NULL) {
+ apple2->useEpple2Cpu(); // set default CPU
+ }
+}
+
+
+
+void E2Command::loadDisk(Slots& slts, int slot, int drive, const std::filesystem::path &media) {
+ Card* card = slts.get(slot);
+ if (!card->hasMedia()) {
+ // TODO if file doesn't exist, name still gets displayed, and there's no error message
+ // TODO error message
+ // std::cerr << "Slot " << slot << " doesn't have a disk controller card" << std::endl;
+ return;
+ }
+
+ if (drive < 1 || 2 < drive) {
+ throw ConfigException("Invalid drive; must be 1 or 2");
+ }
+
+ card->loadMedia(drive - 1, media);
+}
+
+void E2Command::unloadDisk(Slots& slts, int slot, int drive) {
+ Card* card = slts.get(slot);
+ if (!card->hasMedia()) {
+ // TODO do we even need an error here?
+ // std::cerr << "Slot " << slot << " doesn't have a disk controller card" << std::endl;
+ return;
+ }
+
+ if (drive < 1 || 2 < drive) {
+ throw ConfigException("Invalid drive; must be 1 or 2");
+ }
+
+ card->unloadMedia(drive - 1);
+}
+
+void E2Command::saveDisk(Slots& slts, int slot, int drive) {
+ Card* card = slts.get(slot);
+ if (!card->hasMedia()) {
+ // TODO do we even need an error here?
+ // std::cerr << "Slot " << slot << " doesn't have a disk controller card" << std::endl;
+ return;
+ }
+
+ if (drive < 1 || 2 < drive) {
+ throw ConfigException("Invalid drive; must be 1 or 2");
+ }
+
+ card->saveMedia(drive - 1);
+}
+
+
+
+void E2Command::insertCard(const std::string& cardType, const int slot, Slots& slts, ScreenImage& gui, const double random_ones_rate) {
+ if (slot < 0 || 8 <= slot) {
+ throw ConfigException("Invalid slot number");
+ }
+
+ Card* card = nullptr;
+
+ if (cardType == "language") {
+ card = new LanguageCard(gui, slot);
+ } else if (cardType == "firmware") {
+ card = new FirmwareCard(gui, slot);
+ } else if (cardType == "disk") {
+ card = new DiskController(gui, slot, false, random_ones_rate);
+ } else if (cardType == "disk13") {
+ card = new DiskController(gui, slot, true, random_ones_rate);
+ } else if (cardType == "clock") {
+ card = new ClockCard();
+ } else if (cardType == "stdout") {
+ card = new StandardOut();
+ } else if (cardType == "stdin") {
+ card = new StandardIn();
+ } else if (cardType == "empty") {
+ card = 0;
+ } else {
+ throw ConfigException("Invalid card type: " + cardType);
+ }
+
+ if (card) {
+ slts.set(slot, card);
+ } else {
+ slts.remove(slot);
+ }
+}
diff --git a/src/e2command.h b/src/e2command.h
new file mode 100644
index 0000000..02a0ac3
--- /dev/null
+++ b/src/e2command.h
@@ -0,0 +1,71 @@
+/*
+ epple2
+ Copyright (C) 2008 by Christopher A. Mosher
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY, without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ * File: e2command.h
+ * Author: user
+ *
+ * Created on December 11, 2022, 8:21 PM
+ */
+
+#ifndef E2COMMAND_H
+#define E2COMMAND_H
+
+
+
+#include
+#include
+#include
+
+class MemoryRandomAccess;
+class Memory;
+class Slots;
+class ScreenImage;
+class CassetteIn;
+class CassetteOut;
+class Apple2;
+
+
+
+class ConfigException {
+public:
+ const std::string msg;
+ ConfigException(const std::string& msg) : msg(msg) {}
+};
+
+
+
+class E2Command {
+ void tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2);
+
+ void insertCard(const std::string& cardType, const int slot, Slots& slts, ScreenImage& gui, const double random_ones_rate);
+
+ void loadDisk(Slots& slts, int slot, int drive, const std::filesystem::path &media);
+ void unloadDisk(Slots& slts, int slot, int drive);
+ void saveDisk(Slots& slts, int slot, int drive);
+
+public:
+ E2Command();
+ virtual ~E2Command();
+
+ void parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2);
+};
+
+
+
+#endif /* E2COMMAND_H */
diff --git a/src/e2config.cpp b/src/e2config.cpp
index 39e1241..9611a80 100644
--- a/src/e2config.cpp
+++ b/src/e2config.cpp
@@ -17,6 +17,7 @@
*/
#include "e2config.h"
+#include "e2command.h"
#include "E2wxApp.h"
#include "e2filesystem.h"
#include "apple2.h"
@@ -38,11 +39,11 @@
#include
+#include
#include
#include
#include
#include
-#include
#include
#include
#include
@@ -53,17 +54,6 @@ static const wxString DEFAULT_CONFIG_NAME{"epple2"};
-#define K 1024u
-
-static std::uint16_t memory_block_size(const std::string &block_size) {
- if (block_size == "4K") {
- return 4u*K;
- }
- if (block_size == "16K") {
- return 16u*K;
- }
- throw ConfigException("invalid RAM strapping block size (must be 4K or 16K)");
-}
@@ -74,31 +64,16 @@ E2Config::E2Config(const std::filesystem::path& f, bool p): file_path {f}, prefs
E2Config::~E2Config() {
}
-static void strip_comment(std::string& str)
-{
+static void strip_comment(std::string& str) {
const size_t comment = str.find('#');
- if (comment < std::string::npos)
- {
+ if (comment < std::string::npos) {
str.erase(comment);
}
}
-static void trim(std::string& str)
-{
- {
- const size_t p = str.find_first_not_of(" \t");
- if (p < std::string::npos)
- {
- str.erase(0,p);
- }
- }
- {
- const size_t p = str.find_last_not_of(" \t");
- if (p+1 < std::string::npos)
- {
- str.erase(p+1);
- }
- }
+static void trim(std::string& s) {
+ wxString w{s};
+ s = w.Trim(false).Trim(true);
}
@@ -280,8 +255,7 @@ void E2Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& rev
strip_comment(line);
trim(line);
if (!line.empty()) {
- // TODO "parseLine" will become Command::execute, or similar
- parseLine(line, ram, rom, slts, revision, gui, cassetteIn, cassetteOut, apple2);
+ E2Command{}.parseLine(line, ram, rom, slts, revision, gui, cassetteIn, cassetteOut, apple2);
}
std::getline(*p_ifstream_config, line);
}
@@ -290,349 +264,3 @@ void E2Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& rev
// TODO: make sure there is no more than ONE stdin and/or ONE stdout card
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-void E2Config::parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2) {
- try {
- tryParseLine(line, ram, rom, slts, revision, gui, cassetteIn, cassetteOut, apple2);
- } catch (const ConfigException& err) {
- std::cerr << err.msg.c_str() << std::endl;
- }
-}
-
-static std::string filter_row(const std::string &row) {
- if (row.length() != 1) {
- return "";
- }
- return std::string(1, static_cast (std::toupper(row[0])));
-}
-
-void E2Config::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2) {
- std::istringstream tok(line);
-
- std::string cmd;
- tok >> cmd;
- if (cmd == "slot") {
- int slot;
- std::string sCardType;
- tok >> slot >> sCardType;
-
- insertCard(sCardType, slot, slts, gui, tok);
- } else if (cmd == "motherboard") {
- std::string op;
- tok >> op;
- if (op == "ram") {
- /* ram ROW BIT0 [BIT1 [... [BIT7]]]
- * ram e -
- * ram d 4096 MK4096 4K
- * ram c 16K 4116 MK4116 MM5290 16K 16K 16K 16K
- */
- std::string row;
- tok >> row;
- row = filter_row(row);
- if (row != "C" && row != "D" && row != "E") {
- throw ConfigException("expected row to be C, D, or E");
- }
-
- std::string chip_model;
- tok >> chip_model;
- for (std::uint_fast8_t bit = 0u; bit < 8u; ++bit) {
- ram.insert_chip(row, MemoryChip::instance(chip_model), bit);
-
- std::string chip_model_optional;
- tok >> chip_model_optional;
- if (chip_model_optional.length()) {
- chip_model = chip_model_optional;
- }
- }
- } else if (op == "strap") {
- /* strap ROM K start-addr
- * strap e 4K 5000
- * strap d 4K 4000
- * strap c 16K 0000
- */
- std::string row;
- tok >> row;
- row = filter_row(row);
- if (row != "C" && row != "D" && row != "E") {
- throw ConfigException("expected row to be C, D, or E");
- }
-
- std::string block_size;
- tok >> block_size;
- std::uint16_t siz = memory_block_size(block_size);
- unsigned short base(0);
- tok >> std::hex >> base;
- // TODO validate siz/base combination
- ram.strap_to(row, base, siz);
- } else {
- throw ConfigException("error at \"motherboard\"; expected \"ram\" or \"strap\"");
- }
- } else if (cmd == "import") {
- std::string sm;
- tok >> sm;
-
- int slot(-1);
- if (sm == "slot") {
- tok >> slot;
- } else if (sm != "motherboard") {
- throw ConfigException("error at \"" + sm + "\"; expected \"slot #\" or \"motherboard\"");
- }
-
- std::string romtype;
- tok >> romtype;
-
- unsigned short base(0);
- tok >> std::hex >> base;
-
- std::string file;
- std::getline(tok, file);
- trim(file);
- std::ifstream *memfile = new std::ifstream(file.c_str(), std::ios::binary);
- if (!memfile->is_open()) {
- 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 (romtype == "rom") {
- rom.load(base, *memfile);
- } else {
- throw ConfigException("error at \"" + romtype + "\"; expected rom or ram");
- }
- } else {
- if (8 <= slot) {
- throw ConfigException("invalid slot number");
- }
- Card* card = slts.get(slot);
- if (romtype == "rom")
- card->loadRom(base, *memfile);
- else if (romtype == "rom7")
- card->loadSeventhRom(base, *memfile);
- else if (romtype == "rombank")
- card->loadBankRom(base, *memfile);
- else
- throw ConfigException("error at \"" + romtype + "\"; expected rom, rom7, or rombank");
- }
- memfile->close();
- } else if (cmd == "load" || cmd == "save" || cmd == "unload") {
- std::string slotk;
- tok >> slotk;
- if (slotk != "slot") {
- throw ConfigException("error at \"" + slotk + "\"; expected \"slot\"");
- }
-
- int slot(-1);
- tok >> slot;
-
- std::string drivek;
- tok >> drivek;
- if (drivek != "drive") {
- throw ConfigException("error at \"" + drivek + "\"; expected \"drive\"");
- }
-
- int drive(-1);
- tok >> drive;
-
- if (cmd == "load") {
- std::string fn_optional;
- std::getline(tok, fn_optional);
- trim(fn_optional);
- if (fn_optional.length() == 0) {
- gui.exitFullScreen();
- wxFileDialog dlg{nullptr, "Load floppy", "", "", "WOZ 2.0 disk images (*.woz)|*.woz", wxFD_OPEN|wxFD_FILE_MUST_EXIST};
- if (dlg.ShowModal() == wxID_OK) {
- fn_optional = dlg.GetPath().c_str();
- }
- }
- if (fn_optional.length() > 0) {
- loadDisk(slts, slot, drive, fn_optional);
- }
- } else if (cmd == "unload") {
- unloadDisk(slts, slot, drive);
- } else if (cmd == "save") {
- saveDisk(slts, slot, drive);
- }
- } else if (cmd == "revision") {
- tok >> std::hex >> revision;
- } else if (cmd == "cassette") {
- std::string cas;
- tok >> cas;
-
- if (cas == "rewind") {
- cassetteIn.rewind();
- } else if (cas == "tone") {
- cassetteIn.tone();
- } else if (cas == "blank") {
- std::string fcas;
- std::getline(tok, fcas);
- trim(fcas);
- if (!fcas.empty()) {
- cassetteOut.blank(fcas);
- }
- } else if (cas == "load") {
- std::string fn_optional;
- std::getline(tok, fn_optional);
- trim(fn_optional);
- if (fn_optional.length() == 0) {
- gui.exitFullScreen();
- wxFileDialog dlg{nullptr, "Load cassette (audio)", "", "", "WAVE cassette images (*.wav)|*.wav", wxFD_OPEN|wxFD_FILE_MUST_EXIST};
- if (dlg.ShowModal() == wxID_OK) {
- fn_optional = dlg.GetPath().c_str();
- }
- }
- if (fn_optional.length() > 0) {
- cassetteIn.load(fn_optional);
- }
- } else if (cas == "eject") {
- std::string eject;
- tok >> eject;
- if (eject == "in") {
- cassetteIn.eject();
- } else if (eject == "out") {
- cassetteOut.eject();
- } else {
- throw ConfigException("error: unknown cassette to eject: " + eject);
- }
- } else if (cas == "save") {
- cassetteOut.save();
- } else {
- throw ConfigException("error: unknown cassette command: " + cas);
- }
- } else if (cmd == "cpu") {
- std::string cpu;
- tok >> cpu;
- if (apple2 != NULL) {
- if (cpu == "epple2") {
- apple2->useEpple2Cpu();
- } else if (cpu == "visual6502") {
- apple2->useVisual6502Cpu();
- } else {
- throw ConfigException("invalid value for cpu command: " + cpu);
- }
- }
- } else {
- throw ConfigException("Invalid command: " + cmd);
- }
-
- if (apple2 != NULL) {
- apple2->useEpple2Cpu(); // set default CPU
- }
-}
-
-
-
-unsigned char E2Config::disk_mask(0);
-
-void E2Config::loadDisk(Slots& slts, int slot, int drive, const std::string& fnib) {
- if (drive < 1 || 2 < drive) {
- throw ConfigException("Invalid drive; must be 1 or 2");
- }
-
- // TODO if file doesn't exist, name still gets displayed, and there's no error message
- Card* card = slts.get(slot);
- if (!(disk_mask & (1 << slot))) {
- std::cerr << "Slot " << slot << " doesn't have a disk controller card" << std::endl;
- return;
- }
-
- DiskController* controller = (DiskController*) card;
- controller->loadDisk(drive - 1, fnib);
-}
-
-void E2Config::unloadDisk(Slots& slts, int slot, int drive) {
- if (drive < 1 || 2 < drive) {
- throw ConfigException("Invalid drive; must be 1 or 2");
- }
-
- Card* card = slts.get(slot);
- if (!(disk_mask & (1 << slot))) {
- std::cerr << "Slot " << slot << " doesn't have a disk controller card" << std::endl;
- return;
- }
-
- DiskController* controller = (DiskController*) card;
- controller->unloadDisk(drive - 1);
-}
-
-void E2Config::saveDisk(Slots& slts, int slot, int drive) {
- if (drive < 1 || 2 < drive) {
- throw ConfigException("Invalid drive; must be 1 or 2");
- }
- slts.get(slot)->save(drive - 1);
-}
-
-void E2Config::insertCard(const std::string& cardType, int slot, Slots& slts, ScreenImage& gui, std::istringstream& tok) {
- if (slot < 0 || 8 <= slot) {
- throw ConfigException("Invalid slot number");
- }
-
- Card* card;
-
- disk_mask &= ~(1 << slot);
-
- if (cardType == "language") {
- card = new LanguageCard(gui, slot);
- } else if (cardType == "firmware") {
- card = new FirmwareCard(gui, slot);
- } else if (cardType == "disk") {
- // 16-sector LSS ROM
- double random_ones_rate(0.3); // WOZ spec v2.0: 30%
- tok >> random_ones_rate;
- std::cerr << "MC3470: rate of 1 bits during random bit generation: " << random_ones_rate << std::endl;
- card = new DiskController(gui, slot, false, random_ones_rate);
- disk_mask |= (1 << slot);
- } else if (cardType == "disk13") {
- // 13-sector LSS ROM
- double random_ones_rate(0.3); // WOZ spec v2.0: 30%
- tok >> random_ones_rate;
- std::cerr << "MC3470: rate of 1 bits during random bit generation: " << random_ones_rate << std::endl;
- card = new DiskController(gui, slot, true, random_ones_rate);
- disk_mask |= (1 << slot);
- } else if (cardType == "clock") {
- card = new ClockCard();
- } else if (cardType == "stdout") {
- card = new StandardOut();
- } else if (cardType == "stdin") {
- card = new StandardIn();
- } else if (cardType == "empty") {
- card = 0;
- } else {
- throw ConfigException("Invalid card type: " + cardType);
- }
-
- if (card) {
- slts.set(slot, card);
- } else {
- slts.remove(slot);
- }
-}
diff --git a/src/e2config.h b/src/e2config.h
index c819990..ee735e2 100644
--- a/src/e2config.h
+++ b/src/e2config.h
@@ -29,11 +29,6 @@ class CassetteIn;
class CassetteOut;
class Apple2;
-class ConfigException {
-public:
- const std::string msg;
- ConfigException(const std::string& msg) : msg(msg) {}
-};
// TODO split out all static things into their own class (and don't make them static)
// Remember that, besides config, also command line entry calls parseLine
@@ -42,24 +37,17 @@ class E2Config {
private:
const std::filesystem::path file_path;
const bool prefs_only;
- static unsigned char disk_mask;
std::ifstream *openFile();
std::ifstream *openFilePref(const wxString& s_name);
std::ifstream *openFileExternal(const std::filesystem::path& path);
std::ifstream *openFileLegacy();
- static void loadDisk(Slots& slts, int slot, int drive, const std::string& fnib);
- static void unloadDisk(Slots& slts, int slot, int drive);
- static void saveDisk(Slots& slts, int slot, int drive);
- static void insertCard(const std::string& cardType, int slot, Slots& slts, ScreenImage& gui, std::istringstream& tok);
- static void tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2);
public:
E2Config(const std::filesystem::path& f, bool p);
~E2Config();
void parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2);
- static void parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2);
};
#endif
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 9500115..5c07927 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -17,6 +17,7 @@
*/
#include "emulator.h"
#include "e2config.h"
+#include "e2command.h"
#include "e2const.h"
#include
@@ -479,7 +480,7 @@ void Emulator::processCommand() {
return;
}
- E2Config::parseLine(cmdline, this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassetteIn, this->apple2.cassetteOut, NULL);
+ E2Command{}.parseLine(cmdline, this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassetteIn, this->apple2.cassetteOut, NULL);
cmdline.erase(cmdline.begin(), cmdline.end());
}
diff --git a/src/screenimage.cpp b/src/screenimage.cpp
index 3ef11de..6214ab0 100644
--- a/src/screenimage.cpp
+++ b/src/screenimage.cpp
@@ -21,6 +21,7 @@
#include "card.h"
#include "util.h"
#include
+#include
#include
#include
#include
@@ -366,7 +367,7 @@ void ScreenImage::removeCard(const int slot, Card* card /* empty */) {
789012345678901234567890123456789012345678901234567890123456789012345
6: disk][ drive 1M*filename.nib T$FF drive 2M*filename.nib T$FF
*/
-void ScreenImage::setDiskFile(int slot, int drive, const std::string& filepath) {
+void ScreenImage::setDiskFile(int slot, int drive, const std::filesystem::path &filepath) {
std::string f = truncateFilePath(filepath);
int r(R_SLOT + slot);
int c(37 + 32 * drive);
@@ -382,8 +383,8 @@ void ScreenImage::setDiskFile(int slot, int drive, const std::string& filepath)
this->slotnames[slot].replace(c - 20, f.length(), f);
}
-std::string ScreenImage::truncateFilePath(const std::string& filepath) {
- std::string f(filepath);
+std::string ScreenImage::truncateFilePath(const std::filesystem::path& filepath) {
+ std::string f(filepath.c_str());
size_t slash = f.find_last_of("/\\");
if (slash != std::string::npos) {
f = f.substr(slash + 1);
@@ -453,7 +454,7 @@ void ScreenImage::setDirty(int slot, int drive, bool dirty) {
this->slotnames[slot][c - 20] = dirty ? '*' : ' ';
}
-void ScreenImage::setCassetteInFile(const std::string& filepath) {
+void ScreenImage::setCassetteInFile(const std::filesystem::path& filepath) {
std::string f = truncateFilePath(filepath);
int r(65);
int c(85 + 11);
@@ -469,7 +470,7 @@ void ScreenImage::setCassetteInFile(const std::string& filepath) {
this->cassInName.replace(c - 94, f.length(), f);
}
-void ScreenImage::setCassetteOutFile(const std::string& filepath) {
+void ScreenImage::setCassetteOutFile(const std::filesystem::path& filepath) {
std::string f = truncateFilePath(filepath);
int r(66);
int c(85 + 11);
diff --git a/src/screenimage.h b/src/screenimage.h
index 1e447a9..48fb5b6 100644
--- a/src/screenimage.h
+++ b/src/screenimage.h
@@ -19,6 +19,7 @@
#define SCREENIMAGE_H
#include "analogtv.h"
+#include
#include
#include
@@ -45,7 +46,7 @@ private:
std::string cassInName;
std::string cassOutName;
- static std::string truncateFilePath(const std::string& filepath);
+ static std::string truncateFilePath(const std::filesystem::path& filepath);
// TODO some of these methods should be private
public:
@@ -80,7 +81,7 @@ public:
void addkeyCommand(unsigned char key);
void backspaceCommand();
- void setDiskFile(int slot, int drive, const std::string& filename);
+ void setDiskFile(int slot, int drive, const std::filesystem::path& filename);
void setAnnunciator(int ann, bool on);
@@ -90,8 +91,8 @@ public:
void setIO(int slot, int drive, bool on);
void setDirty(int slot, int drive, bool dirty);
- void setCassetteInFile(const std::string& filepath);
- void setCassetteOutFile(const std::string& filepath);
+ void setCassetteInFile(const std::filesystem::path& filepath);
+ void setCassetteOutFile(const std::filesystem::path& filepath);
void setCassetteDirty(bool dirty); // cassette out only
void setCassettePos(unsigned int pos, unsigned int siz); // cassette in only
diff --git a/src/slots.cpp b/src/slots.cpp
index 919c197..2a8068d 100644
--- a/src/slots.cpp
+++ b/src/slots.cpp
@@ -134,8 +134,8 @@ void Slots::forceGuiUpdate()
}
void Slots::save(int unit) {
- for (std::vector::iterator i = this->cards.begin(); i != this->cards.end(); ++i) {
- (*i)->save(unit);
+ for (auto &i : this->cards) {
+ i->saveMedia(unit);
}
}
@@ -154,14 +154,12 @@ bool isAnyDiskDriveMotorOn()
return on.inhibit;
}
*/
-struct Slots_Card_isDirty
-{
- bool dirty;
- Slots_Card_isDirty():dirty(false) {}
- void operator() (Card* p) { if (p->isDirty()) dirty = true; }
-};
-bool Slots::isDirty()
-{
- return std::for_each(this->cards.begin(),this->cards.end(),Slots_Card_isDirty()).dirty;
+bool Slots::isDirty() {
+ for (auto &i : this->cards) {
+ if (i->isMediaDirty()) {
+ return true;
+ }
+ }
+ return false;
}
diff --git a/src/slots.h b/src/slots.h
index 515d02c..ca5794f 100644
--- a/src/slots.h
+++ b/src/slots.h
@@ -24,9 +24,7 @@
class ScreenImage;
-class Slots
-{
-private:
+class Slots {
ScreenImage& gui;
EmptySlot empty;
std::vector cards;
@@ -35,7 +33,7 @@ public:
Slots(ScreenImage& gui);
~Slots();
- void tick();
+ void tick();
unsigned char io(const int islot, const int iswch, const unsigned char b, const bool writing);
void reset();
unsigned char readRom(const int islot, const unsigned short addr, const unsigned char data);
@@ -46,8 +44,8 @@ public:
Card* get(const int slot);
void remove(const int slot);
bool isDirty();
- void save(int unit);
- void forceGuiUpdate();
+ void save(int unit);
+ void forceGuiUpdate();
};
#endif