diff --git a/conf/epple2.conf.in b/conf/epple2.conf.in index 4a4ef73..1b4630b 100644 --- a/conf/epple2.conf.in +++ b/conf/epple2.conf.in @@ -11,6 +11,8 @@ +cpu epple2 + # standard 48K RAM with mixed chip brands motherboard ram E MM5290 MM5290 MM5290 MK4116 MM5290 MM5290 MM5290 MK4116 motherboard strap E 16K 8000 diff --git a/conf/epple2.visual6502.conf.in b/conf/epple2.visual6502.conf.in new file mode 100644 index 0000000..ee36a9d --- /dev/null +++ b/conf/epple2.visual6502.conf.in @@ -0,0 +1,16 @@ +# Use the Visual 6502 emulation algorithm (from http://www.visual6502.org/). +# WARNING: this is EXTREMELY SLOW + + + +cpu visual6502 + + + +motherboard ram E MM5290 MM5290 MM5290 MK4116 MM5290 MM5290 MM5290 MK4116 +motherboard strap E 16K 8000 +motherboard ram D MM5290 MM5290 MK4116 MK4116 MM5290 MK4116 MM5290 MCM4116 +motherboard strap D 16K 4000 +motherboard ram C MK4116 MK4116 MM5290 MM5290 MM5290 MM5290 MM5290 MM5290 +motherboard strap C 16K 0000 +import motherboard rom 2C00 ${LIBDIR}/epple2/system/epple2sys.a65 diff --git a/docs/usermanual.md b/docs/usermanual.md index 1bb15a7..22c6bbd 100644 --- a/docs/usermanual.md +++ b/docs/usermanual.md @@ -121,6 +121,26 @@ strap c 4K 0000 +#### cpu + +The `cpu` command chooses which CPU emulator to run with. + +``` conf +cpu epple2 +``` + +Valid values are: + +`epple2` The standard, faster, albeit less accurate, high-level emulator. Works for 99.99% of known cases. +This is the default value, used when the `cpu` command is not present. + +`visual6502` The emulator based on the algorithm and transistor circuitry from http://www.visual6502.org/. +WARNING: this emulator is *extremely slow*, but absolutely 100% accurate to the original MOS6502. + +Note: the CPU cannot be changed in the user interface, only in the configuration file. + + + #### import The `import` command imports a binary image file into the emulated Apple's ROMs. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0831653..12be51c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,17 +72,9 @@ add_executable(epple2 ${sources}) find_package(SDL2 CONFIG) message(STATUS "SDL2_INCLUDE_DIRS: ${SDL2_INCLUDE_DIRS}") -message(STATUS "SDL2_LIBRARIES: ${SDL2_LIBRARIES}") target_include_directories(epple2 PRIVATE ${SDL2_INCLUDE_DIRS}) +message(STATUS "SDL2_LIBRARIES: ${SDL2_LIBRARIES}") target_link_libraries(epple2 ${SDL2_LIBRARIES}) target_compile_features(epple2 PRIVATE cxx_std_17) target_compile_definitions(epple2 PRIVATE ETCDIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SYSCONFDIR}") - -option(USE_EMU "Use http://www.visual6502.org/ CPU (will run slowly)") -if (USE_EMU) - message(STATUS "USE_EMU") - target_compile_definitions(epple2 PRIVATE USE_EMU) - # target_compile_definitions(epple2 PRIVATE TRACESEG) - target_compile_definitions(epple2 PRIVATE TRACEREG) -endif() diff --git a/src/Emu6502.h b/src/Emu6502.h index ab27c07..971b021 100644 --- a/src/Emu6502.h +++ b/src/Emu6502.h @@ -1,4 +1,4 @@ -/* +/* * File: Emu6502.h * Author: Christopher * @@ -8,6 +8,7 @@ #ifndef EMU6502_H #define EMU6502_H +#include "abstractcpu.h" #include "Cpu6502Helper.h" #include "Cpu6502.h" #include "Trace.h" @@ -19,7 +20,7 @@ class AddressBus; -class Emu6502 { +class Emu6502 : public AbstractCpu { public: Emu6502(std::istream& transistors, AddressBus& mem) : tn(transistors, segs, transes), c(tn), trace(segs, transes, c), cpu(mem, trace, c), cpuhelper(cpu, c) { diff --git a/src/abstractcpu.h b/src/abstractcpu.h new file mode 100644 index 0000000..3fb75bc --- /dev/null +++ b/src/abstractcpu.h @@ -0,0 +1,29 @@ +/* + epple2 + Copyright (C) 2008 by Christopher A. Mosher + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY, without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef ABSTRACTCPU_H +#define ABSTRACTCPU_H + +class AbstractCpu { +public: + virtual ~AbstractCpu() {}; + virtual void powerOn() = 0; + virtual void reset() = 0; + virtual void tick() = 0; +}; + +#endif diff --git a/src/apple2.cpp b/src/apple2.cpp index 12ca666..ccf58ef 100644 --- a/src/apple2.cpp +++ b/src/apple2.cpp @@ -48,12 +48,8 @@ Apple2::Apple2(KeypressQueue& keypresses, PaddleButtonStates& paddleButtonStates addressBus(gui,revision,ram,rom,kbd,videoMode,paddles,paddleButtonStates,speaker,cassetteIn,cassetteOut,slts), picgen(tv,videoMode,revision), video(videoMode,addressBus,picgen,textRows), -#ifdef USE_EMU - transistors("transistors"), - cpu(transistors,addressBus), -#else - cpu(addressBus), -#endif + transistors("transistors"), + cpu(NULL), powerUpReset(*this), revision(1) { @@ -63,9 +59,22 @@ Apple2::~Apple2() { } +void Apple2::useEpple2Cpu() { + if (this->cpu == NULL) { + std::cout << "Using fast Epple2 CPU emulator" << std::endl; + this->cpu = new CPU(this->addressBus); + } +} + +void Apple2::useVisual6502Cpu() { + if (this->cpu == NULL) { + std::cout << "Using http://www.visual6502.org/ CPU emulation (which will be slow)." << std::endl; + this->cpu = new Emu6502(this->transistors, this->addressBus); + } +} void Apple2::tick() { - this->cpu.tick(); + this->cpu->tick(); this->slts.tick(); this->video.tick(); this->paddles.tick(); @@ -81,7 +90,7 @@ void Apple2::tick() { void Apple2::powerOn() { this->ram.powerOn(); - this->cpu.powerOn(); + this->cpu->powerOn(); this->videoMode.powerOn(); this->video.powerOn(); this->picgen.powerOn(); @@ -95,6 +104,6 @@ void Apple2::powerOff() void Apple2::reset() { - this->cpu.reset(); + this->cpu->reset(); this->slts.reset(); } diff --git a/src/apple2.h b/src/apple2.h index 69e721f..4d9d64c 100644 --- a/src/apple2.h +++ b/src/apple2.h @@ -28,6 +28,7 @@ #include "picturegenerator.h" #include "textcharacters.h" #include "video.h" +#include "abstractcpu.h" #include "cpu.h" #include "paddles.h" #include "paddlebuttonstates.h" @@ -56,12 +57,8 @@ class Apple2 : public Timable PictureGenerator picgen; TextCharacters textRows; Video video; -#ifdef USE_EMU - std::ifstream transistors; - Emu6502 cpu; -#else - CPU cpu; -#endif + std::ifstream transistors; + AbstractCpu* cpu; PowerUpReset powerUpReset; int revision; @@ -69,6 +66,9 @@ public: Apple2(KeypressQueue& keypresses, PaddleButtonStates& paddleButtonStates, AnalogTV& tv, HyperMode& fhyper, KeyboardBufferMode& buffered, ScreenImage& gui); ~Apple2(); + void useEpple2Cpu(); + void useVisual6502Cpu(); + void powerOn(); void powerOff(); void reset(); diff --git a/src/configep2.cpp b/src/configep2.cpp index b2f2d84..06c4aa1 100644 --- a/src/configep2.cpp +++ b/src/configep2.cpp @@ -17,6 +17,7 @@ */ #include "configep2.h" +#include "apple2.h" #include "memory.h" #include "memoryrandomaccess.h" #include "slots.h" @@ -93,14 +94,8 @@ static void trim(std::string& str) } } -void Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut) +void Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2) { -#ifdef USE_EMU - std::cout << "Running with http://www.visual6502.org/ CPU emulation (which will be slow)." << std::endl; -#endif - - - std::ifstream* pConfig; std::string path(this->file_path); @@ -192,7 +187,7 @@ void Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revis trim(line); if (!line.empty()) { - parseLine(line,ram,rom,slts,revision,gui,cassetteIn,cassetteOut); + parseLine(line,ram,rom,slts,revision,gui,cassetteIn,cassetteOut,apple2); } std::getline(*pConfig,line); } @@ -201,11 +196,11 @@ void Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revis // TODO: make sure there is no more than ONE stdin and/or ONE stdout card } -void Config::parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut) +void Config::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); + tryParseLine(line,ram,rom,slts,revision,gui,cassetteIn,cassetteOut,apple2); } catch (const ConfigException& err) { @@ -220,7 +215,7 @@ static std::string filter_row(const std::string &row) { return std::string(1, static_cast(std::toupper(row[0]))); } -void Config::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut) +void Config::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); @@ -456,10 +451,28 @@ void Config::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memo 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 Config::loadDisk(Slots& slts, int slot, int drive, const std::string& fnib) diff --git a/src/configep2.h b/src/configep2.h index 6b80128..3d6e3d2 100644 --- a/src/configep2.h +++ b/src/configep2.h @@ -25,6 +25,7 @@ class Slots; class ScreenImage; class CassetteIn; class CassetteOut; +class Apple2; class ConfigException { public: @@ -41,14 +42,14 @@ private: 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); + static void tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2); public: Config(const std::string& file_path); ~Config(); - void parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut); - static void parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut); + void parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2); + static void parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2); }; #endif diff --git a/src/cpu.h b/src/cpu.h index ed07e1a..df0b031 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -18,10 +18,11 @@ #ifndef CPU_H #define CPU_H +#include "abstractcpu.h" class AddressBus; +#include -class CPU -{ +class CPU : public AbstractCpu { private: enum { MEMORY_LIM = 1 << 0x10 }; enum { IRQ_VECTOR = MEMORY_LIM-2 }; // or BRK @@ -43,7 +44,7 @@ private: bool pendingIRQ; bool pendingNMI; bool pendingReset; - + bool started; unsigned char a; @@ -205,7 +206,7 @@ private: public: CPU(AddressBus& addressBus); - ~CPU(); + virtual ~CPU(); void powerOn(); void reset(); diff --git a/src/emulator.cpp b/src/emulator.cpp index 11a7666..3779488 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -72,7 +72,7 @@ void Emulator::powerOffComputer() { } void Emulator::config(Config& cfg) { - cfg.parse(this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassetteIn, this->apple2.cassetteOut); + cfg.parse(this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassetteIn, this->apple2.cassetteOut, &this->apple2); this->apple2.ram.dump_config(); } @@ -444,7 +444,7 @@ void Emulator::processCommand() { return; } - Config::parseLine(cmdline, this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassetteIn, this->apple2.cassetteOut); + Config::parseLine(cmdline, this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassetteIn, this->apple2.cassetteOut, NULL); cmdline.erase(cmdline.begin(), cmdline.end()); } diff --git a/src/emulator.h b/src/emulator.h index c297567..c15a355 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -60,7 +60,7 @@ class Emulator void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent); void cmdKey(const SDL_KeyboardEvent& keyEvent); void processCommand(); - bool isSafeToQuit(); + bool isSafeToQuit(); public: Emulator(); diff --git a/src/powerupreset.cpp b/src/powerupreset.cpp index bd87a68..d30d9dc 100644 --- a/src/powerupreset.cpp +++ b/src/powerupreset.cpp @@ -44,9 +44,5 @@ void PowerUpReset::tick() void PowerUpReset::powerOn() { -#ifdef USE_EMU - this->pendingTicks = 99; // TODO REMOVE THIS -#else this->pendingTicks = (int)(E2Const::AVG_CPU_HZ*.3); // U.A.II, p. 7-15 -#endif }