/*
* 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.
*/
#ifndef CPU_H
#define CPU_H
#include <QObject>
#include <QVector>
#include <QTimer>
#include "io.h"
//! This class contains CPU contex and functions
class CPU : public QObject
{
Q_OBJECT
public:
static const int FREQUENCY = 1000000; //!< Base frequency of CPU
static const int INTERVAL = 125; //!< Time in milliseconds between every step of the emulation
static const int TICKS_PER_INTERVAL = ((FREQUENCY * INTERVAL) / 1000); //!< How many clock ticks of CPU will be processed in a single step of the emulation
static const int CLOCK_INTERVAL = 125; //!< How many milliseconds is needed to toggle the clock pin status on the IO BUS. Default frequency is 8 Hz
static const int CLOCK_TICKS_PER_INTERVAL = ((FREQUENCY * CLOCK_INTERVAL) / 1000); //!< How many clock ticks of CPU is needed to toggle the clock pin status on the IO BUS.
static const int MEMORY_PAGE_SIZE = 256; //!< Memory page size
static const int MEMORY_PAGE_QUANTITY = 128; //!< Quantity of pages in one memory block
static const int MEMORY_SIZE = (MEMORY_PAGE_SIZE * MEMORY_PAGE_QUANTITY); //!< Size of the memory block
static const int ROM_OS_ADDRESS = 0x0000; //!< Start of ROM OS address space
static const int ROM_APP_ADDRESS = 0x8000; //!< Start of ROM Application address space
static const int RAM_OS_ADDRESS = 0x0000; //!< Start of RAM OS address space
static const int RAM_SP_ADDRESS = 0x0f00; //!< Start of RAM Stack Pointer address space
static const int RAM_APP_ADDRESS = 0x1000; //!< Start of RAM Application address space
static const int STACK_PAGE = 0x0f; //!< Page number of the stack
static const int UROM_ADDRESS_CYCLE_MASK = 0x0f; //!< Micro-step mask of the CPU step for uROM
static const int UROM_ADDRESS_INSTRUCTION_POSITION = 0; //!< Offset of instruction for uROM
static const int UROM_ADDRESS_CYCLE_POSITION = 8; //!< Offset of micro-step for uROM
static const int UROM_ADDRESS_FLAG_C_POSITION = 12; //!< Offset of C Flag for uROM
static const int UROM_ADDRESS_FLAG_Z_POSITION = 13; //!< Offset of Z Flag for uROM
static const int UROM_0_BUS_AR_MASK = 0b00000111; //!< BUS A Read mask for first uROM bank
static const int UROM_0_BUS_AW_MASK = 0b01111000; //!< BUS A Write mask for first uROM bank
static const int UROM_0_BUS_B_MASK = 0b10000000; //!< BUS B mask for first uROM bank
static const int UROM_0_BUS_AR_POSITION = 0; //!< BUS A Read offset for first uROM bank
static const int UROM_0_BUS_AW_POSITION = 3; //!< BUS A Write offset for first uROM bank
static const int UROM_0_BUS_B_POSITION = 7; //!< BUS B offset for first uROM bank
static const int UROM_1_BUS_C_MASK = 0b00000011; //!< BUS C mask for second uROM bank
static const int UROM_1_ALU_S_MASK = 0b00111100; //!< ALU S Flags mask for second uROM bank
static const int UROM_1_ALU_M_MASK = 0b01000000; //!< ALU M Flag mask for second uROM bank
static const int UROM_1_ALU_C_MASK = 0b10000000; //!< ALU C Flag mask for second uROM bank
static const int UROM_1_BUS_C_POSITION = 0; //!< BUS C offset for second uROM bank
static const int UROM_1_ALU_S_POSITION = 2; //!< ALU S Flags offset for second uROM bank
static const int INSTRUCTION_REG_ABXY_FIRST_MASK = 0b00000011; //!< First argument ABXY Register mask for opcode
static const int INSTRUCTION_REG_ABXY_SECOND_MASK = 0b00001100; //!< Second argument ABXY Register mask for opcode
static const int INSTRUCTION_REG_AB_MASK = 0b00000100; //!< Second argument AB Register mask for opcode
static const int INSTRUCTION_REG_ABXY_FIRST_OFFSET = 0; //!< First argument ABXY Register offset for opcode
static const int INSTRUCTION_REG_ABXY_SECOND_OFFSET = 2; //!< Second argument ABXY Register offset for opcode
static const int INSTRUCTION_REG_AB_OFFSET = 2; //!< Second argument AB Register offset for opcode
static const int ALU_4BIT_MASK = 0x0f; //!< ALU Input/Output mask for lower and higher part of byte
static const int ALU_4BIT_OFFSET = 4; //!< ALU Input/Output offset for lower and higher part of byte
//! Memory buffer
struct Memory
{
QVector<unsigned char> data = QVector<unsigned char>(MEMORY_SIZE); //!< Memory data
};
//! Register buffer
struct Reg
{
unsigned char i; //!< Instruction register
bool c; //!< Carry Flag register
bool z; //!< Zero Flag register
unsigned char pch; //!< Program Counter High register
unsigned char pcl; //!< Program Counter Low register
unsigned char sph; //!< Stack Pointer High register
unsigned char spl; //!< Stack Pointer Low register
unsigned char maxSpl; //!< Maximum reached value of Stack Pointer
unsigned char a; //!< A register
unsigned char b; //!< B register
unsigned char x; //!< X register
unsigned char y; //!< Y rRegister
unsigned char in; //!< Input register
unsigned char out; //!< Output register
unsigned char d; //!< Hidden Data register
unsigned char t; //!< ALU Output register
unsigned char mah; //!< Memory Address High register
unsigned char mal; //!<Memory Address Low register
};
CPU(QObject *parent = nullptr);
void setUrom0(const CPU::Memory &urom);
void setUrom1(const CPU::Memory &urom);
void setRom(const CPU::Memory &rom);
void run();
void step();
void pause();
void stop();
const CPU::Reg &getReg() const;
unsigned long long getTicks() const;
const CPU::Memory &getRom() const;
const CPU::Memory &getRamLow() const;
const CPU::Memory &getRamHigh() const;
private:
//! Line select defines for the main reading data bus
enum class BusAR
{
ABXY = 0, //!< Main and auxiliary registers
D, //!< Hidden data register
IN, //!< Input register
T, //!< ALU temp register
Ram, //!< RAM access
Flash, //!< FLASH access
PCL, //!< Program Counter Low register
PCH //!< Program Counter High register
};
//! Line select defines for the main writing data bus
enum class BusAW
{
None = 0, //!< Not selected
ABXY, //!< Main and auxiliary registers
D, //!< Hidden data register
OUT, //!< Output register
ALU_T, //!< ALU operation
RPC, //!< Reset Program Counter
I, //!< Instruction register
Ram, //!< RAM access
PCL, //!< Program Counter Low register
PCH, //!< Program Counter High register
MAL, //!< Memory Address Low register
MAH, //!< Memory Address High register
PC_PLUS, //!< Increse Program Counter
SP_PLUS, //!< Increse Stack Pointer
SP_MINUS, //!< Decrese Stack Pointer
RPC_PLUS //!< Reset the step counter and increse Program Counter
};
//! Line select defines for the secondary reading data bus
enum class BusB
{
AB = 0, //!< Main registers
D //!< Hidden data register
};
//! Line select defines for the addressing data bus
enum class BusC
{
PC = 0, //!< Full 16 bit Program Counter register
MA, //!< Full 16 bit Memory Address register
SP, //!< Full 16 bit Stack Pointer register
XY //!< Full 16 bit auxiliary register
};
void reset();
unsigned char alu(unsigned char a, unsigned char b, unsigned char s, bool m, bool &c, bool &z);
bool stepMode; //!< Step mode enabler for emulation
unsigned long long ticks; //!< Counter of clock ticks of CPU
QTimer timer; //!< Timer for executing emulation steps
Memory urom0; //!< First uROM memory buffer
Memory urom1; //!< Second uROM memory buffer
Memory rom; //!< ROM memory buffer
Memory ramLow; //!< Lower 32 kB of RAM buffer
Memory ramHigh; //!< Higher 32 kB of RAM buffer
Reg reg; //!< Register buffer
signals:
void outSignal(unsigned char out);
void updateSignal();
public slots:
void inSlot(unsigned char in);
private slots:
void emulation();
};
#endif