/* * Author: Pawel Jablonski * E-mail: pj@xirx.net * WWW: xirx.net * GIT: git.xirx.net * * License: You can use this code however you like * but leave information about the original author. * Code is free for non-commercial and commercial use. */ #include "cpu.h" /** * Constructor for the CPU class * * @param parent Parent object */ CPU::CPU(QObject *parent) : QObject(parent) { this->urom0.data.fill(0); this->urom1.data.fill(0); this->rom.data.fill(0); this->reset(); QObject::connect(&this->timer, SIGNAL(timeout()), this, SLOT(emulation())); } /** * Set first uROM memory data * * @param urom uROM data */ void CPU::setUrom0(const CPU::Memory &urom) { this->urom0 = urom; } /** * Set second uROM memory data * * @param urom uROM data */ void CPU::setUrom1(const CPU::Memory &urom) { this->urom1 = urom; } /** * Set ROM memory data * * @param rom ROM data */ void CPU::setRom(const CPU::Memory &rom) { this->rom = rom; } //! Run executing emulation void CPU::run() { this->stepMode = false; this->timer.setSingleShot(false); this->timer.setInterval(INTERVAL); this->timer.start(); } //! Run only one CPU step of emulation void CPU::step() { this->stepMode = true; this->timer.setSingleShot(true); this->timer.start(0); } //! Pause executing emulation void CPU::pause() { this->timer.stop(); } //! Stop executing emulation void CPU::stop() { this->timer.stop(); this->reset(); } /** * Get register buffer * * @return Register buffer */ const CPU::Reg &CPU::getReg() const { return(this->reg); } /** * Get ticks counter value * * @return Tick counter value */ unsigned long long CPU::getTicks() const { return(this->ticks); } /** * Get ROM buffer * * @return ROM buffer */ const CPU::Memory &CPU::getRom() const { return(this->rom); } /** * Get lower RAM buffer * * @return Lower RAM buffer */ const CPU::Memory &CPU::getRamLow() const { return(this->ramLow); } /** * Get higher RAM buffer * * @return Higher RAM buffer */ const CPU::Memory &CPU::getRamHigh() const { return(this->ramHigh); } //! Stop executing and reset data of emulation void CPU::reset() { this->stepMode = false; this->ticks = 0; this->timer.stop(); this->ramLow.data.fill(0); this->ramHigh.data.fill(0); this->reg.i = 0; this->reg.c = false; this->reg.z = false; this->reg.pch = 0; this->reg.pcl = 0; this->reg.sph = STACK_PAGE; this->reg.spl = 0; this->reg.maxSpl = 0; this->reg.a = 0; this->reg.b = 0; this->reg.x = 0; this->reg.y = 0; this->reg.in = 0; this->reg.out = 0; this->reg.d = 0; this->reg.t = 0; this->reg.mah = 0; this->reg.mal = 0; } /** * Compute logic or arithmetic using ALU. It emulates functions of the 74181 chip. * * @param a First operant * @param b Second operant * @param s Operation type selector in range 0-15 * @param m Operatiom mode selector. "0" for arithmetic and "1" for logic mode. * @param c Carry flag * @param z Zero flag * * @return Computed value */ unsigned char CPU::alu(unsigned char a, unsigned char b, unsigned char s, bool m, bool &c, bool &z) { QVector inA(4); QVector inB(4); QVector inS(4); inA[0] = ((a & (1 << 0)) != 0); inA[1] = ((a & (1 << 1)) != 0); inA[2] = ((a & (1 << 2)) != 0); inA[3] = ((a & (1 << 3)) != 0); inB[0] = ((b & (1 << 0)) != 0); inB[1] = ((b & (1 << 1)) != 0); inB[2] = ((b & (1 << 2)) != 0); inB[3] = ((b & (1 << 3)) != 0); inS[0] = ((s & (1 << 0)) != 0); inS[1] = ((s & (1 << 1)) != 0); inS[2] = ((s & (1 << 2)) != 0); inS[3] = ((s & (1 << 3)) != 0); QVector> l1(4, QVector(5)); for(int i = 0; i < 4; i++) { l1[i][0] = inA[i]; l1[i][1] = (inB[i] & inS[0]); l1[i][2] = ((!inB[i]) & inS[1]); l1[i][3] = ((!inB[i]) & inA[i] & inS[2]); l1[i][4] = (inA[i] & inB[i] & inS[3]); } QVector> l2(4, QVector(2)); for(int i = 0; i < 4; i++) { l2[i][0] = !(l1[i][0] | l1[i][1] | l1[i][2]); l2[i][1] = !(l1[i][3] | l1[i][4]); } QVector l3(20); l3[0] = !(c & (!m)); l3[1] = !(l2[0][0]); l3[2] = ((!m) & l2[0][0]); l3[3] = ((!m) & l2[0][1] & c); l3[4] = !(l2[1][0]); l3[5] = ((!m) & l2[1][0]); l3[6] = ((!m) & l2[0][0] & l2[1][1]); l3[7] = ((!m) & l2[0][1] & l2[1][1] & c); l3[8] = !(l2[2][0]); l3[9] = ((!m) & l2[2][0]); l3[10] = ((!m) & l2[1][0] & l2[2][1]); l3[11] = ((!m) & l2[0][0] & l2[1][1] & l2[2][1]); l3[12] = ((!m) & l2[0][1] & l2[1][1] & l2[2][1] & c); l3[13] = !(l2[3][0]); l3[14] = !(l2[0][1] & l2[1][1] & l2[2][1] & l2[3][1]); l3[15] = !(l2[0][1] & l2[1][1] & l2[2][1] & l2[3][1] & c); l3[16] = (l2[0][0] & l2[1][1] & l2[2][1] & l2[3][1]); l3[17] = (l2[1][0] & l2[2][1] & l2[3][1]); l3[18] = (l2[2][0] & l2[3][1]); l3[19] = (l2[3][0]); QVector l4(8); l4[0] = (l3[1] & l2[0][1]); l4[1] = !(l3[2] | l3[3]); l4[2] = (l3[4] & l2[1][1]); l4[3] = !(l3[5] | l3[6] | l3[7]); l4[4] = (l3[8] & l2[2][1]); l4[5] = !(l3[9] | l3[10] | l3[11] | l3[12]); l4[6] = (l3[13] & l2[3][1]); l4[7] = !(l3[16] | l3[17] | l3[18] | l3[19]); QVector f(4); f[0] = (l3[0] ^ l4[0]); f[1] = (l4[1] ^ l4[2]); f[2] = (l4[3] ^ l4[4]); f[3] = (l4[5] ^ l4[6]); z = (f[0] & f[1] & f[2] & f[3]); c = ((!l3[15]) | (!l4[7])); unsigned char out = 0; out += (f[0] ? 1 : 0); out += (f[1] ? 2 : 0); out += (f[2] ? 4 : 0); out += (f[3] ? 8 : 0); return(out); } /** * Set Input register value * * @param in Input register value */ void CPU::inSlot(unsigned char in) { if(this->ticks > 0) { this->reg.in = ((this->reg.in & (1 << 4)) | (in & ~(1 << 4))); } } //! Process one step of the emulation void CPU::emulation() { unsigned long long tick = 0; unsigned char uromCycle = 0; int uromAddress; unsigned char urom0; unsigned char urom1; BusAR busAR; BusAW busAW; BusB busB; BusC busC; int address; unsigned char aluS; bool aluM; bool aluC; unsigned char busARregABXY; unsigned char busAWregABXY; unsigned char busBregAB; unsigned char valueAR = 0; unsigned char valueB = 0; while(tick < TICKS_PER_INTERVAL) { do { uromAddress = (static_cast(this->reg.i) << UROM_ADDRESS_INSTRUCTION_POSITION); uromAddress += (static_cast(uromCycle & UROM_ADDRESS_CYCLE_MASK) << UROM_ADDRESS_CYCLE_POSITION); uromAddress += (static_cast(this->reg.c ? 1 : 0) << UROM_ADDRESS_FLAG_C_POSITION); uromAddress += (static_cast(this->reg.z ? 1 : 0) << UROM_ADDRESS_FLAG_Z_POSITION); urom0 = this->urom0.data[uromAddress]; urom1 = this->urom1.data[uromAddress]; uromCycle++; busAR = static_cast((urom0 & UROM_0_BUS_AR_MASK) >> UROM_0_BUS_AR_POSITION); busAW = static_cast((urom0 & UROM_0_BUS_AW_MASK) >> UROM_0_BUS_AW_POSITION); busB = static_cast((urom0 & UROM_0_BUS_B_MASK) >> UROM_0_BUS_B_POSITION); busC = static_cast((urom1 & UROM_1_BUS_C_MASK) >> UROM_1_BUS_C_POSITION); aluS = ((urom1 & UROM_1_ALU_S_MASK) >> 2); aluM = ((urom1 & UROM_1_ALU_M_MASK) != 0); aluC = ((urom1 & UROM_1_ALU_C_MASK) != 0); if((busAR == BusAR::ABXY) && (busAW == BusAW::ABXY)) { busARregABXY = ((this->reg.i & INSTRUCTION_REG_ABXY_SECOND_MASK) >> INSTRUCTION_REG_ABXY_SECOND_OFFSET); } else { busARregABXY = ((this->reg.i & INSTRUCTION_REG_ABXY_FIRST_MASK) >> INSTRUCTION_REG_ABXY_FIRST_OFFSET); } busAWregABXY = (this->reg.i & INSTRUCTION_REG_ABXY_FIRST_MASK); busBregAB = ((this->reg.i & INSTRUCTION_REG_AB_MASK) >> INSTRUCTION_REG_AB_OFFSET); switch(busC) { case BusC::PC : address = ((static_cast(this->reg.pch) << 8) + static_cast(this->reg.pcl)); break; case BusC::MA : address = ((static_cast(this->reg.mah) << 8) + static_cast(this->reg.mal)); break; case BusC::SP : address = ((static_cast(this->reg.sph) << 8) + static_cast(this->reg.spl)); break; case BusC::XY : address = ((static_cast(this->reg.y) << 8) + static_cast(this->reg.x)); break; } switch(busAR) { case BusAR::ABXY : switch(busARregABXY) { case 0 : valueAR = this->reg.a; break; case 1 : valueAR = this->reg.b; break; case 2 : valueAR = this->reg.x; break; case 3 : valueAR = this->reg.y; break; } break; case BusAR::D : valueAR = this->reg.d; break; case BusAR::IN : valueAR = this->reg.in; break; case BusAR::T : valueAR = this->reg.t; break; case BusAR::Ram : if(address < MEMORY_SIZE) { valueAR = this->ramLow.data[address]; } else { valueAR = this->ramHigh.data[address - MEMORY_SIZE]; } break; case BusAR::Flash : if(address < MEMORY_SIZE) { valueAR = this->rom.data[address]; } else { valueAR = this->ramHigh.data[address - MEMORY_SIZE]; } break; case BusAR::PCL : valueAR = this->reg.pcl; break; case BusAR::PCH : valueAR = this->reg.pch; break; } switch(busB) { case BusB::AB : switch(busBregAB) { case 0 : valueB = this->reg.a; break; case 1 : valueB = this->reg.b; break; } break; case BusB::D : valueB = this->reg.d; break; } switch(busAW) { case BusAW::None : break; case BusAW::ABXY : switch(busAWregABXY) { case 0 : this->reg.a = valueAR; break; case 1 : this->reg.b = valueAR; break; case 2 : this->reg.x = valueAR; break; case 3 : this->reg.y = valueAR; break; } break; case BusAW::D : this->reg.d = valueAR; break; case BusAW::OUT : { this->reg.out = valueAR; emit outSignal(this->reg.out); } break; case BusAW::ALU_T : { QVector z(2); bool c = aluC; unsigned char value = 0; value = this->alu((valueAR & ALU_4BIT_MASK), (valueB & ALU_4BIT_MASK), aluS, aluM, c, z[0]); value += (this->alu((valueAR >> ALU_4BIT_OFFSET), (valueB >> ALU_4BIT_OFFSET), aluS, aluM, c, z[1]) << ALU_4BIT_OFFSET); this->reg.t = value; this->reg.c = c; this->reg.z = (z[0] & z[1]); } break; case BusAW::RPC : uromCycle = 0; break; case BusAW::I : this->reg.i = valueAR; break; case BusAW::Ram : if(address < MEMORY_SIZE) { this->ramLow.data[address] = valueAR; } else { this->ramHigh.data[address - MEMORY_SIZE] = valueAR; } break; case BusAW::PCL : this->reg.pcl = valueAR; break; case BusAW::PCH : this->reg.pch = valueAR; break; case BusAW::MAL : this->reg.mal = valueAR; break; case BusAW::MAH : this->reg.mah = valueAR; break; case BusAW::PC_PLUS : { unsigned int pcAddress = ((static_cast(this->reg.pch) << 8) + static_cast(this->reg.pcl) + 1); this->reg.pch = static_cast(pcAddress >> 8); this->reg.pcl = static_cast(pcAddress & 0xff); } break; case BusAW::SP_PLUS : this->reg.spl++; this->reg.maxSpl = qMax(this->reg.maxSpl, this->reg.spl); break; case BusAW::SP_MINUS : this->reg.spl--; break; case BusAW::RPC_PLUS : { unsigned int pcAddress = ((static_cast(this->reg.pch) << 8) + static_cast(this->reg.pcl) + 1); this->reg.pch = static_cast(pcAddress >> 8); this->reg.pcl = static_cast(pcAddress & 0xff); uromCycle = 0; } break; } tick++; this->ticks++; if((this->ticks % CLOCK_TICKS_PER_INTERVAL) == 0) { this->reg.in ^= IO::IN_CLOCK_BIT; } } while(uromCycle > 0); if(this->stepMode) { break; } } emit updateSignal(); }