refactor: split commands out of config, clean up some parsing, add media concept to card abstraction

This commit is contained in:
Christopher A. Mosher 2022-12-11 22:07:50 -05:00
parent b138df21eb
commit 46582c6566
13 changed files with 588 additions and 490 deletions

View File

@ -77,6 +77,7 @@ diskcontroller.cpp
drive.cpp drive.cpp
drivemotor.cpp drivemotor.cpp
e2config.cpp e2config.cpp
e2command.cpp
e2filesystem.cpp e2filesystem.cpp
e2string.cpp e2string.cpp
E2wxApp.cpp E2wxApp.cpp

View File

@ -14,96 +14,89 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "card.h" #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; 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; this->activeSeventhRom = true;
return this->rom.read(address, data); 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; this->activeSeventhRom = false;
} } else if (this->activeSeventhRom && hasSeventhRom()) {
else if (this->activeSeventhRom && hasSeventhRom())
{
*pb = this->seventhRom.read(address, *pb); *pb = this->seventhRom.read(address, *pb);
} }
} }
void Card::loadRom(const unsigned short base, std::istream& in) void Card::loadRom(const unsigned short base, std::istream& in) {
{ this->rom.load(base, in);
this->rom.load(base,in);
} }
void Card::loadSeventhRom(const unsigned short base, std::istream& in) void Card::loadSeventhRom(const unsigned short base, std::istream& in) {
{ this->seventhRom.load(base, in);
this->seventhRom.load(base,in);
} }
bool Card::inhibitMotherboardRom() {
bool Card::inhibitMotherboardRom()
{
return false; return false;
} }
void Card::ioBankRom(const unsigned short addr, unsigned char* const pb, const bool write) {
}
void Card::loadBankRom(const unsigned short base, std::istream& in) {
void Card::ioBankRom(const unsigned short /*addr*/, unsigned char* const /*pb*/, const bool /*write*/) // TODO? maybe just do nothing
{ // throw ConfigException("This card has no $D000 ROM");
} }
void Card::loadBankRom(const unsigned short /*base*/, std::istream& /*in*/) bool Card::isMediaDirty() {
{
throw ConfigException("This card has no $D000 ROM");
}
std::string Card::getName()
{
return "";
}
bool Card::isDirty()
{
return false; 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) {
} }

View File

@ -14,19 +14,19 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef CARD_H #ifndef CARD_H
#define CARD_H #define CARD_H
#include "memory.h" #include "memory.h"
#include <filesystem>
#include <istream> #include <istream>
#include <string> #include <string>
class Card class Card {
{
private:
bool activeSeventhRom; bool activeSeventhRom;
protected: protected:
Memory rom; Memory rom;
Memory seventhRom; Memory seventhRom;
@ -34,20 +34,27 @@ protected:
public: public:
Card(); Card();
virtual ~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 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 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 readSeventhRom(const unsigned short address, unsigned char* const pb);
virtual void loadRom(const unsigned short base, std::istream& in); virtual void loadRom(const unsigned short base, std::istream& in);
virtual void loadSeventhRom(const unsigned short base, std::istream& in); virtual void loadSeventhRom(const unsigned short base, std::istream& in);
virtual bool inhibitMotherboardRom(); virtual bool inhibitMotherboardRom();
virtual void ioBankRom(const unsigned short addr, unsigned char* const pb, const bool write); 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 void loadBankRom(const unsigned short base, std::istream& in);
virtual bool isDirty(); virtual bool isMediaDirty();
virtual void save(int unit);
virtual std::string getName(); virtual bool hasMedia();
virtual void loadMedia(int unit, const std::filesystem::path &media);
virtual void unloadMedia(int unit);
virtual void saveMedia(int unit);
}; };
#endif #endif

View File

@ -21,6 +21,7 @@
#include "wozfile.h" #include "wozfile.h"
#include "lss.h" #include "lss.h"
#include "screenimage.h" #include "screenimage.h"
#include <filesystem>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <cstdint> #include <cstdint>
@ -94,21 +95,25 @@ public:
this->gui.setCurrentDrive(this->slot,getCurrentDriveNumber(),getTrack(),false); this->gui.setCurrentDrive(this->slot,getCurrentDriveNumber(),getTrack(),false);
} }
void loadDisk(unsigned char drive, const std::string& fnib) { virtual bool hasMedia() override {
if (!this->getDrive(drive).loadDisk(fnib)) { return true;
}
virtual void loadMedia(int unit, const std::filesystem::path &media) override {
if (!this->getDrive(unit).loadDisk(media)) {
return; return;
} }
this->gui.setDiskFile(this->slot,drive,fnib); this->gui.setDiskFile(this->slot,unit,media);
this->gui.setDirty(this->slot,getCurrentDriveNumber(),false); this->gui.setDirty(this->slot,getCurrentDriveNumber(),false);
} }
void unloadDisk(unsigned char drive) { virtual void unloadMedia(int unit) override {
this->getDrive(drive).unloadDisk(); this->getDrive(unit).unloadDisk();
this->gui.setDiskFile(this->slot,drive,""); this->gui.setDiskFile(this->slot,unit,std::filesystem::path{});
this->gui.setDirty(this->slot,getCurrentDriveNumber(),false); this->gui.setDirty(this->slot,getCurrentDriveNumber(),false);
} }
void save(int drive) { virtual void saveMedia(int drive) override {
this->getDrive(drive).saveDisk(); this->getDrive(drive).saveDisk();
this->gui.setDirty(this->slot,getCurrentDriveNumber(),false); this->gui.setDirty(this->slot,getCurrentDriveNumber(),false);
} }
@ -137,7 +142,7 @@ public:
return this->currentDrive->isWriteProtected(); return this->currentDrive->isWriteProtected();
} }
bool isDirty() { bool isMediaDirty() {
return isModified() || isModifiedOther(); return isModified() || isModifiedOther();
} }

406
src/e2command.cpp Normal file
View File

@ -0,0 +1,406 @@
/*
epple2
Copyright (C) 2008 by Christopher A. Mosher <cmosher01@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <wx/filedlg.h>
#include <wx/string.h>
#include <filesystem>
#include <iostream>
#include <fstream>
#include <string>
#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<char> (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);
}
}

71
src/e2command.h Normal file
View File

@ -0,0 +1,71 @@
/*
epple2
Copyright (C) 2008 by Christopher A. Mosher <cmosher01@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
/*
* File: e2command.h
* Author: user
*
* Created on December 11, 2022, 8:21 PM
*/
#ifndef E2COMMAND_H
#define E2COMMAND_H
#include <filesystem>
#include <sstream>
#include <string>
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 */

View File

@ -17,6 +17,7 @@
*/ */
#include "e2config.h" #include "e2config.h"
#include "e2command.h"
#include "E2wxApp.h" #include "E2wxApp.h"
#include "e2filesystem.h" #include "e2filesystem.h"
#include "apple2.h" #include "apple2.h"
@ -38,11 +39,11 @@
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <filesystem>
#include <iostream> #include <iostream>
#include <istream> #include <istream>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <filesystem>
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include <cctype> #include <cctype>
@ -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() { E2Config::~E2Config() {
} }
static void strip_comment(std::string& str) static void strip_comment(std::string& str) {
{
const size_t comment = str.find('#'); const size_t comment = str.find('#');
if (comment < std::string::npos) if (comment < std::string::npos) {
{
str.erase(comment); str.erase(comment);
} }
} }
static void trim(std::string& str) static void trim(std::string& s) {
{ wxString w{s};
{ s = w.Trim(false).Trim(true);
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);
}
}
} }
@ -280,8 +255,7 @@ void E2Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& rev
strip_comment(line); strip_comment(line);
trim(line); trim(line);
if (!line.empty()) { if (!line.empty()) {
// TODO "parseLine" will become Command::execute, or similar E2Command{}.parseLine(line, ram, rom, slts, revision, gui, cassetteIn, cassetteOut, apple2);
parseLine(line, ram, rom, slts, revision, gui, cassetteIn, cassetteOut, apple2);
} }
std::getline(*p_ifstream_config, line); 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 // 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<char> (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);
}
}

View File

@ -29,11 +29,6 @@ class CassetteIn;
class CassetteOut; class CassetteOut;
class Apple2; 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) // 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 // Remember that, besides config, also command line entry calls parseLine
@ -42,24 +37,17 @@ class E2Config {
private: private:
const std::filesystem::path file_path; const std::filesystem::path file_path;
const bool prefs_only; const bool prefs_only;
static unsigned char disk_mask;
std::ifstream *openFile(); std::ifstream *openFile();
std::ifstream *openFilePref(const wxString& s_name); std::ifstream *openFilePref(const wxString& s_name);
std::ifstream *openFileExternal(const std::filesystem::path& path); std::ifstream *openFileExternal(const std::filesystem::path& path);
std::ifstream *openFileLegacy(); 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: public:
E2Config(const std::filesystem::path& f, bool p); E2Config(const std::filesystem::path& f, bool p);
~E2Config(); ~E2Config();
void parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2); 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 #endif

View File

@ -17,6 +17,7 @@
*/ */
#include "emulator.h" #include "emulator.h"
#include "e2config.h" #include "e2config.h"
#include "e2command.h"
#include "e2const.h" #include "e2const.h"
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
@ -479,7 +480,7 @@ void Emulator::processCommand() {
return; 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()); cmdline.erase(cmdline.begin(), cmdline.end());
} }

View File

@ -21,6 +21,7 @@
#include "card.h" #include "card.h"
#include "util.h" #include "util.h"
#include <SDL.h> #include <SDL.h>
#include <filesystem>
#include <iostream> #include <iostream>
#include <ctime> #include <ctime>
#include <sstream> #include <sstream>
@ -366,7 +367,7 @@ void ScreenImage::removeCard(const int slot, Card* card /* empty */) {
789012345678901234567890123456789012345678901234567890123456789012345 789012345678901234567890123456789012345678901234567890123456789012345
6: disk][ drive 1M*filename.nib T$FF drive 2M*filename.nib T$FF 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); std::string f = truncateFilePath(filepath);
int r(R_SLOT + slot); int r(R_SLOT + slot);
int c(37 + 32 * drive); 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); this->slotnames[slot].replace(c - 20, f.length(), f);
} }
std::string ScreenImage::truncateFilePath(const std::string& filepath) { std::string ScreenImage::truncateFilePath(const std::filesystem::path& filepath) {
std::string f(filepath); std::string f(filepath.c_str());
size_t slash = f.find_last_of("/\\"); size_t slash = f.find_last_of("/\\");
if (slash != std::string::npos) { if (slash != std::string::npos) {
f = f.substr(slash + 1); f = f.substr(slash + 1);
@ -453,7 +454,7 @@ void ScreenImage::setDirty(int slot, int drive, bool dirty) {
this->slotnames[slot][c - 20] = 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); std::string f = truncateFilePath(filepath);
int r(65); int r(65);
int c(85 + 11); int c(85 + 11);
@ -469,7 +470,7 @@ void ScreenImage::setCassetteInFile(const std::string& filepath) {
this->cassInName.replace(c - 94, f.length(), f); 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); std::string f = truncateFilePath(filepath);
int r(66); int r(66);
int c(85 + 11); int c(85 + 11);

View File

@ -19,6 +19,7 @@
#define SCREENIMAGE_H #define SCREENIMAGE_H
#include "analogtv.h" #include "analogtv.h"
#include <filesystem>
#include <vector> #include <vector>
#include <string> #include <string>
@ -45,7 +46,7 @@ private:
std::string cassInName; std::string cassInName;
std::string cassOutName; 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 // TODO some of these methods should be private
public: public:
@ -80,7 +81,7 @@ public:
void addkeyCommand(unsigned char key); void addkeyCommand(unsigned char key);
void backspaceCommand(); 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); void setAnnunciator(int ann, bool on);
@ -90,8 +91,8 @@ public:
void setIO(int slot, int drive, bool on); void setIO(int slot, int drive, bool on);
void setDirty(int slot, int drive, bool dirty); void setDirty(int slot, int drive, bool dirty);
void setCassetteInFile(const std::string& filepath); void setCassetteInFile(const std::filesystem::path& filepath);
void setCassetteOutFile(const std::string& filepath); void setCassetteOutFile(const std::filesystem::path& filepath);
void setCassetteDirty(bool dirty); // cassette out only void setCassetteDirty(bool dirty); // cassette out only
void setCassettePos(unsigned int pos, unsigned int siz); // cassette in only void setCassettePos(unsigned int pos, unsigned int siz); // cassette in only

View File

@ -134,8 +134,8 @@ void Slots::forceGuiUpdate()
} }
void Slots::save(int unit) { void Slots::save(int unit) {
for (std::vector<Card*>::iterator i = this->cards.begin(); i != this->cards.end(); ++i) { for (auto &i : this->cards) {
(*i)->save(unit); i->saveMedia(unit);
} }
} }
@ -154,14 +154,12 @@ bool isAnyDiskDriveMotorOn()
return on.inhibit; 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() bool Slots::isDirty() {
{ for (auto &i : this->cards) {
return std::for_each(this->cards.begin(),this->cards.end(),Slots_Card_isDirty()).dirty; if (i->isMediaDirty()) {
return true;
}
}
return false;
} }

View File

@ -24,9 +24,7 @@
class ScreenImage; class ScreenImage;
class Slots class Slots {
{
private:
ScreenImage& gui; ScreenImage& gui;
EmptySlot empty; EmptySlot empty;
std::vector<Card*> cards; std::vector<Card*> cards;
@ -35,7 +33,7 @@ public:
Slots(ScreenImage& gui); Slots(ScreenImage& gui);
~Slots(); ~Slots();
void tick(); void tick();
unsigned char io(const int islot, const int iswch, const unsigned char b, const bool writing); unsigned char io(const int islot, const int iswch, const unsigned char b, const bool writing);
void reset(); void reset();
unsigned char readRom(const int islot, const unsigned short addr, const unsigned char data); unsigned char readRom(const int islot, const unsigned short addr, const unsigned char data);
@ -46,8 +44,8 @@ public:
Card* get(const int slot); Card* get(const int slot);
void remove(const int slot); void remove(const int slot);
bool isDirty(); bool isDirty();
void save(int unit); void save(int unit);
void forceGuiUpdate(); void forceGuiUpdate();
}; };
#endif #endif