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
drivemotor.cpp
e2config.cpp
e2command.cpp
e2filesystem.cpp
e2string.cpp
E2wxApp.cpp

View File

@ -14,96 +14,89 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
*/
#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) {
}

View File

@ -14,19 +14,19 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
*/
#ifndef CARD_H
#define CARD_H
#include "memory.h"
#include <filesystem>
#include <istream>
#include <string>
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

View File

@ -21,6 +21,7 @@
#include "wozfile.h"
#include "lss.h"
#include "screenimage.h"
#include <filesystem>
#include <string>
#include <iostream>
#include <cstdint>
@ -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();
}

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 "e2command.h"
#include "E2wxApp.h"
#include "e2filesystem.h"
#include "apple2.h"
@ -38,11 +39,11 @@
#include <boost/log/trivial.hpp>
#include <filesystem>
#include <iostream>
#include <istream>
#include <fstream>
#include <sstream>
#include <filesystem>
#include <string>
#include <stdexcept>
#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() {
}
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<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 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

View File

@ -17,6 +17,7 @@
*/
#include "emulator.h"
#include "e2config.h"
#include "e2command.h"
#include "e2const.h"
#include <wx/msgdlg.h>
@ -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());
}

View File

@ -21,6 +21,7 @@
#include "card.h"
#include "util.h"
#include <SDL.h>
#include <filesystem>
#include <iostream>
#include <ctime>
#include <sstream>
@ -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);

View File

@ -19,6 +19,7 @@
#define SCREENIMAGE_H
#include "analogtv.h"
#include <filesystem>
#include <vector>
#include <string>
@ -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

View File

@ -134,8 +134,8 @@ void Slots::forceGuiUpdate()
}
void Slots::save(int unit) {
for (std::vector<Card*>::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;
}

View File

@ -24,9 +24,7 @@
class ScreenImage;
class Slots
{
private:
class Slots {
ScreenImage& gui;
EmptySlot empty;
std::vector<Card*> 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