diff --git a/.gitignore b/.gitignore index 50701e1..5ccae9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.o +*.d v6502 v6502.exe /nbproject/ diff --git a/Circuit.cpp b/Circuit.cpp new file mode 100644 index 0000000..9c34d6c --- /dev/null +++ b/Circuit.cpp @@ -0,0 +1,78 @@ +/* + * File: Circuit.cpp + * Author: Christopher + * + * Created on December 12, 2013, 7:04 PM + */ + +#include "Circuit.h" +#include "trans.h" + +Circuit::Circuit(Segment* extendFrom, Segment* VSS, Segment* VCC) : VSS(VSS), VCC(VCC) { + extend(extendFrom); +} + +/* + * Adds segment extendFrom, and all segments electrically connected to it. + * This happens recursively, but we don't recurse past ground or voltage supply. + */ +void Circuit::extend(Segment* extendFrom) { + const std::pair < std::set::iterator, bool> ret = this->segs.insert(extendFrom); + if (!ret.second) { + return; + } + if (extendFrom == this->VCC || extendFrom == this->VSS) { + return; + } + + /* + * For every ON transistor this seg is connected to via a leg (source or + * drain), add the seg that's connected to the OTHER leg of the transistor. + * This is a RECURSIVE addition. + * + * Also note that, upon system startup, all transistors are initialized + * to OFF, so at the time of the very first recalcAll call, *no* other + * segments will be added here. + */ + for (std::set::const_iterator iTrn = extendFrom->c1c2s.begin(); iTrn != extendFrom->c1c2s.end(); ++iTrn) { + const Trans * t(*iTrn); + if (t->on) { + if (t->c1 == extendFrom) { + extend(t->c2); + } else if (t->c2 == extendFrom) { + extend(t->c1); + } + } + } +} + +/* + * Upon system startup, this will return the "pullup" value of + * each segment, except for VCC and VSS (which will of course be + * ON and OFF respectively). + */ +bool Circuit::getValue() { + /* If group contains ground, it's OFF, */ + if (contains(this->VSS)) { + return false; + }/* otherwise, if group contains voltage supply, it's ON. */ + else if (contains(this->VCC)) { + return true; + } + + + + for (std::set::const_iterator iSeg = this->segs.begin(); iSeg != this->segs.end(); ++iSeg) { + Segment * s(*iSeg); + if (s->pullup) { + return true; + } + if (s->pulldown) { + return false; + } + if (s->on) { + return true; + } + } + return false; +} diff --git a/Circuit.h b/Circuit.h new file mode 100644 index 0000000..a55ac17 --- /dev/null +++ b/Circuit.h @@ -0,0 +1,42 @@ +/* + * File: Circuit.h + * Author: Christopher + * + * Created on December 12, 2013, 7:04 PM + */ + +#ifndef CIRCUIT_H +#define CIRCUIT_H + +#include + +class Segment; + +class Circuit { +public: + Circuit(Segment* extendFrom, Segment* VSS, Segment* VCC); + + virtual ~Circuit() { + } + + bool getValue(); + std::set::iterator begin() { return this->segs.begin(); } + std::set::iterator end() { return this->segs.end(); } + +private: + Circuit(const Circuit&); + Circuit& operator=(const Circuit&); + + void extend(Segment* extendFrom); + + bool contains(Segment* s) const { + return this->segs.find(s) != this->segs.end(); + } + + std::set segs; + Segment* VSS; + Segment* VCC; +}; + +#endif /* CIRCUIT_H */ + diff --git a/Cpu6502.cpp b/Cpu6502.cpp new file mode 100644 index 0000000..2b2a154 --- /dev/null +++ b/Cpu6502.cpp @@ -0,0 +1,204 @@ +/* + * File: Cpu6502.cpp + * Author: Christopher + * + * Created on December 12, 2013, 10:14 PM + */ + +#include "Cpu6502.h" +#include "TransNetwork.h" +#include "trans.h" +#include "AddressBus.h" +#include "Trace.h" +#include + + + + + + + +#define TRACEREG 1 +//#define TRACESEG 1 +//#define TRACEMEM 1 + +#ifdef TRACEREG +#define dumpRegs() this->trace.dumpRegisters() +#else +#define dumpRegs() +#endif + +#ifdef TRACESEG +#define dumpSegs() dumpSegments() +#else +#define dumpSegs() +#endif + + + + + + +void Cpu6502::powerOn() { + std::cout << "initial state" << std::endl; + dumpRegs(); + dumpSegs(); + + + + /* + * Since we use segs[CLK0].on as our own + * temporary variable (see "step" method), we + * need to initialize it here, to "phase one". + */ + segs[n->CLK0].on = true; + + + + std::cout << "setting input pins..." << std::endl; + initPins(); + + std::cout << "initial full calculation..." << std::endl; + recalcAll(); + dumpRegs(); + dumpSegs(); +} + +void Cpu6502::initPins() { + // set voltage supply and ground. + setSeg(n->VCC, true); + setSeg(n->VSS, false); + + // don't do the set-overflow overriding functionality + setSeg(n->SO, false); + + // ready to run (i.e., do not do single-stepping of instructions) + setSeg(n->RDY, true); + + // pull up to indicate that we are not interrupting now + setSeg(n->IRQ, true); + setSeg(n->NMI, true); + + + /* + * RES_BAR pin means "not resetting". Since it is a negated pin, pulling it low means "resetting" + * and pulling it high means "not resetting" or equivalently "running". + */ + + /* + * RES_BAR false: resetting now (i.e., in power-up now; pull high to begin normal operation) + * We want to hold RES_BAR low for a while, indicating power-up phase during which the + * CPU does not start up normal operations yet. The caller can set RES_BAR high (by calling + * reset) whenever he is ready to start the CPU running. + */ + setSeg(n->RES, false); +} + +void Cpu6502::reset() { + setSeg(n->RES, true); + recalc(n->RES); +} + +void Cpu6502::tick() { + step(); + step(); +} + +void Cpu6502::step() { + /* + * We cheat a little bit here: instead of requiring the + * caller to toggle clock-zero pin, we let him just call + * "step" and *we* keep track of which phase we are in. + * To do this, we just use the CLK0 segment value (as + * a kind of temporary variable), and just toggle it in + * order to know which phase we are going into. + * + * The real 6502, of course, does not do this. + */ + const bool nextPhase = !segs[n->CLK0].on; + + clock(nextPhase); + rw(); + + dumpRegs(); + dumpSegs(); +} + +void Cpu6502::clock(bool phase) { + setSeg(n->CLK0, phase); + recalc(n->CLK0); +} + +void Cpu6502::rw() { + // database read/write happens during Clock Phase 2 (only) + if (segs[n->CLK2OUT].on) { + readBus(); + + std::set s; + addDataToRecalc(s); + recalc(s); + + writeBus(); + } +} + + + + + + + + +void Cpu6502::readBus() { + if (this->transNetwork.segs.c->RW->on) { + this->transNetwork.segs.setDataSegs(read(this->transNetwork.segs.rAddr())); + } +} + +void Cpu6502::writeBus() { + if (!this->transNetwork.segs.c->RW->on) { + write(this->transNetwork.segs.rAddr(), this->transNetwork.segs.rData()); + } +} + + + + + + +unsigned char Cpu6502::read(unsigned short addr) { + const unsigned char x = this->addressBus.read(addr); +#ifdef TRACEMEM + std::cout << "-------------------------------------------------- "; + pHex(x); + std::cout << "<"; + pHexw(addr); + std::cout << std::endl; +#endif + return x; +} + +void Cpu6502::write(unsigned short addr, unsigned char data) { + this->addressBus.write(addr, data); +#ifdef TRACEMEM + std::cout << "-------------------------------------------------- "; + pHex(data); + std::cout << ">"; + pHexw(addr); + std::cout << std::endl; +#endif +} + + + + + + + +void Cpu6502::recalcAll() { + std::set riSeg; + for (int iSeg = 0; iSeg < segs.size(); ++iSeg) { + addRecalc(iSeg,riSeg); + } + recalc(riSeg); +} diff --git a/Cpu6502.h b/Cpu6502.h new file mode 100644 index 0000000..0e4b83f --- /dev/null +++ b/Cpu6502.h @@ -0,0 +1,46 @@ +/* + * File: Cpu6502.h + * Author: Christopher + * + * Created on December 12, 2013, 10:14 PM + */ + +#ifndef CPU6502_H +#define CPU6502_H + +class TransNetwork; +class AddressBus; +class Trace; + +class Cpu6502 { +public: + + Cpu6502(TransNetwork& transNetwork, AddressBus& addressBus, Trace& trace) : transNetwork(transNetwork), addressBus(addressBus), trace(trace) { + } + + virtual ~Cpu6502() { + } + +private: + Cpu6502(const Cpu6502&); + Cpu6502& operator=(const Cpu6502&); + + void powerOn(); + void initPins(); + void reset(); + void tick(); + void step(); + void clock(bool phase); + void rw(); + void readBus(); + void writeBus(); + unsigned char read(unsigned short addr); + void write(unsigned short addr, unsigned char data); + + TransNetwork& transNetwork; + AddressBus& addressBus; + + Trace& trace; +}; + +#endif /* CPU6502_H */ diff --git a/Cpu6502Helper.cpp b/Cpu6502Helper.cpp new file mode 100644 index 0000000..7c50a89 --- /dev/null +++ b/Cpu6502Helper.cpp @@ -0,0 +1,18 @@ +/* + * File: Cpu6502Helper.cpp + * Author: Christopher + * + * Created on December 12, 2013, 10:22 PM + */ + +#include "Cpu6502Helper.h" + +Cpu6502Helper::Cpu6502Helper() { +} + +Cpu6502Helper::Cpu6502Helper(const Cpu6502Helper& orig) { +} + +Cpu6502Helper::~Cpu6502Helper() { +} + diff --git a/Cpu6502Helper.h b/Cpu6502Helper.h new file mode 100644 index 0000000..2ad637c --- /dev/null +++ b/Cpu6502Helper.h @@ -0,0 +1,21 @@ +/* + * File: Cpu6502Helper.h + * Author: Christopher + * + * Created on December 12, 2013, 10:22 PM + */ + +#ifndef CPU6502HELPER_H +#define CPU6502HELPER_H + +class Cpu6502Helper { +public: + Cpu6502Helper(); + Cpu6502Helper(const Cpu6502Helper& orig); + virtual ~Cpu6502Helper(); +private: + +}; + +#endif /* CPU6502HELPER_H */ + diff --git a/Makefile b/Makefile index 92d6c9a..05dd785 100644 --- a/Makefile +++ b/Makefile @@ -1,25 +1,24 @@ CXXFLAGS=-g -std=c++11 +SRCS = v6502.cpp cpu.cpp nodes.cpp trans.cpp SegmentCache.cpp TransNetwork.cpp Trace.cpp Circuit.cpp StateCalculator.cpp Cpu6502.cpp +OBJS = $(SRCS:.cpp=.o) +DEPS = $(SRCS:.cpp=.d) + +.cpp.o: + $(COMPILE.cpp) -MMD $(OUTPUT_OPTION) $< + +.PHONY: all clean + + + all: v6502 -v6502: v6502.o cpu.o nodes.o trans.o SegmentCache.o TransNetwork.o Trace.o - g++ $^ -o $@ +v6502: $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET) -v6502.o: v6502.cpp cpu.h addressbus.h TransNetwork.h +-include $(DEPS) -cpu.o: cpu.cpp cpu.h addressbus.h nodes.h -nodes.o: nodes.cpp nodes.h - -trans.o: trans.cpp trans.h - -SegmentCache.o: SegmentCache.cpp SegmentCache.h - -TransNetwork.o: TransNetwork.cpp TransNetwork.h trans.h - -Trace.o: Trace.cpp Trace.h SegmentCache.h clean: - -rm *.o - -rm v6502 - -rm v6502.exe + -rm -f v6502 v6502.exe $(OBJS) $(DEPS) diff --git a/StateCalculator.cpp b/StateCalculator.cpp new file mode 100644 index 0000000..d04a5f7 --- /dev/null +++ b/StateCalculator.cpp @@ -0,0 +1,93 @@ +/* + * File: StateCalculator.cpp + * Author: Christopher + * + * Created on December 12, 2013, 8:29 PM + */ + +#include "StateCalculator.h" +#include "Circuit.h" +#include "trans.h" +#include "cpu.h" +#include + +StateCalculator::StateCalculator(Segment* VSS, Segment* VCC) : VSS(VSS), VCC(VCC) { +} + +//void StateCalculator::recalcAll() { +// std::set riSeg; +// for (int iSeg = 0; iSeg < segs.size(); ++iSeg) { +// addRecalc(iSeg,riSeg); +// } +// recalc(riSeg); +//} + +void StateCalculator::recalc(Segment* seg, Segment* VSS, Segment* VCC) { + std::set rSeg; + rSeg.insert(seg); + recalc(rSeg,VSS,VCC); +} + +/* + * Recalculate segment states (on/off), based on the fact that the segments + * in riSeg have just changed state. Keep track of which other segments are + * affected, and repeat the process on those segments. Repeat until no more + * segments change state. + */ +#define SANE (100) + +void StateCalculator::recalc(const std::set& rSeg, Segment* VSS, Segment* VCC) { + int sanity(0); + + std::set riSegRecalc(rSeg); + while (!riSegRecalc.empty()) { + if (++sanity >= SANE) { + throw "ERROR: reached maximum iteration limit while recalculating CPU state"; + } + + StateCalculator c(VSS, VCC); + for (std::set::const_iterator is = riSegRecalc.begin(); is != riSegRecalc.end(); ++is) { + c.recalcNode(*is); + } + riSegRecalc = c.getChanged(); + } +} + +/* + * Gets group of segments currently electrically connected to iSeg, + * gets what their group value is (or should be), goes through all + * those segments and sets their "on" value. For all connected gates, + * turn on/off, and indicate that the segments connected to those + * transistors' source and drain legs have changed, by adding them + * to riSegChanged. + */ +void StateCalculator::recalcNode(Segment* seg) { + if (!(seg == this->VSS || seg == this->VCC)) { + Circuit c(seg, this->VSS, this->VCC); + + for (std::set::iterator is = c.begin(); is != c.end(); ++is) { + Segment * s(*is); + if (s->on != c.getValue()) { + s->on = c.getValue(); + for (std::set::iterator it = s->gates.begin(); it != s->gates.end(); ++it) { + Trans * t(*it); + setTrans(t, c.getValue()); + } + } + } + } +} + +void StateCalculator::setTrans(Trans* t, const bool on) { + if (t->on != on) { + t->on = on; + addRecalc(t->c1); + addRecalc(t->c2); + } +} + +void StateCalculator::addRecalc(Segment* seg) { + if (!(seg == this->VSS || seg == this->VCC)) { + this->segs.insert(seg); + } +} diff --git a/StateCalculator.h b/StateCalculator.h new file mode 100644 index 0000000..4716b0d --- /dev/null +++ b/StateCalculator.h @@ -0,0 +1,43 @@ +/* + * File: StateCalculator.h + * Author: Christopher + * + * Created on December 12, 2013, 8:29 PM + */ + +#ifndef STATECALCULATOR_H +#define STATECALCULATOR_H + +#include + +class Segment; +class Trans; + +class StateCalculator { +public: + static void recalc(const std::set& rSeg, Segment* VSS, Segment* VCC); + static void recalc(Segment* seg, Segment* VSS, Segment* VCC); + +private: + std::set segs; + Segment* VSS; + Segment* VCC; + + StateCalculator(Segment* VSS, Segment* VCC); + + virtual ~StateCalculator() { + } + + StateCalculator(const StateCalculator&); + StateCalculator& operator=(const StateCalculator&); + + void recalcNode(Segment* seg); + void setTrans(Trans* t, const bool on); + void addRecalc(Segment* seg); + + std::set getChanged() { + return this->segs; + } +}; + +#endif /* STATECALCULATOR_H */ diff --git a/trans.h b/trans.h index d75739c..345c658 100644 --- a/trans.h +++ b/trans.h @@ -36,7 +36,7 @@ public: }; class Trans { -private: +public: Segment* c1; Segment* gate; Segment* c2; diff --git a/v6502.cpp b/v6502.cpp index a163589..32f227e 100644 --- a/v6502.cpp +++ b/v6502.cpp @@ -12,6 +12,7 @@ #include "addressbus.h" #include "cpu.h" +#include "Cpu6502.h" #include "TransNetwork.h" //memory[0xFF] = 0x68; // PLA @@ -44,8 +45,8 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } TransNetwork tn(if_trans); - SegmentCache::Common* n = tn.segs.c; - n->CLK0->on = true; + + Cpu6502 cpu(tn); } int xxxmain(int argc, char *argv[]) {