From 46582c6566bb818a0d862bdd1666546ccd7e65fe Mon Sep 17 00:00:00 2001 From: "Christopher A. Mosher" Date: Sun, 11 Dec 2022 22:07:50 -0500 Subject: [PATCH] refactor: split commands out of config, clean up some parsing, add media concept to card abstraction --- CMakeLists.txt | 1 + src/card.cpp | 99 +++++------ src/card.h | 27 +-- src/diskcontroller.h | 21 ++- src/e2command.cpp | 406 +++++++++++++++++++++++++++++++++++++++++++ src/e2command.h | 71 ++++++++ src/e2config.cpp | 388 +---------------------------------------- src/e2config.h | 12 -- src/emulator.cpp | 3 +- src/screenimage.cpp | 11 +- src/screenimage.h | 9 +- src/slots.cpp | 20 +-- src/slots.h | 10 +- 13 files changed, 588 insertions(+), 490 deletions(-) create mode 100644 src/e2command.cpp create mode 100644 src/e2command.h 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