refactor: split commands out of config, clean up some parsing, add media concept to card abstraction
This commit is contained in:
parent
b138df21eb
commit
46582c6566
|
@ -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
|
||||||
|
|
99
src/card.cpp
99
src/card.cpp
|
@ -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) {
|
||||||
}
|
}
|
||||||
|
|
27
src/card.h
27
src/card.h
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 */
|
388
src/e2config.cpp
388
src/e2config.cpp
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
10
src/slots.h
10
src/slots.h
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue