/************************************************************************/
/****** Title : Microprocessor Simulation ******/
/****** ******/
/****** Author : Stephen Griffin ******/
/************************************************************************/
/************************************************************************/
/****** Pre-Processor Directives ******/
/************************************************************************/
#include <stdio.h>
#define LDA 0
#define STA 1
#define BRK 2
#define JMP 3
#define BRA 4
#define ADD 5
#define SUB 6
#define MUL 7
#define DIV 8
#define CLA 9
#define CLC 10
#define DMP 11
//Extra Instructions
#define AND 12
#define DEC 13
#define BRO 14
#define BRU 15
#define MEMSIZE 16
/************************************************************************/
/****** Function Prototypes ******/
/************************************************************************/
char Load_Memory_Data(void); //Load memory image from file.
void Dump_Memory_Data(void); //Save mem and regs to file.
void Initialise_CPU(void); //Set registers to 0.
void Fetch(void);
void Decode(unsigned char *, unsigned char *);
void Execute(unsigned char, unsigned char, unsigned char *);
void Display_Registers(unsigned char);
/************************************************************************/
/****** Global Variables ******/
/************************************************************************/
struct
{
unsigned char MEM[MEMSIZE];
struct
{
unsigned char ir;
unsigned char acc;
unsigned char pc;
unsigned char mar;
unsigned char mdr;
unsigned char unf;
unsigned char ovf;
unsigned char crf;
}reg;
}cpu;
/************************************************************************/
/****** Main Function ******/
/************************************************************************/
void main(void)
{
unsigned char opcode=0, operand=0, fdeloop=1; //Local variables
if(Load_Memory_Data()) /* only continue if memory data loaded */
{
Initialise_CPU(); /* set all regs to zero */
Display_Registers(1); //display register names
/* Loop - Fetch, Decode, Execute */
while((fdeloop)&&(cpu.reg.pc<MEMSIZE))
{
Fetch();
Decode(&opcode, &operand);
Execute(opcode, operand, &fdeloop);
Display_Registers(0); //display register values
}
}
}
/************************************************************************/
/****** Load Memory data ******/
/************************************************************************/
char Load_Memory_Data(void)
{
FILE *fp; /* define file pointer */
unsigned char c, addr=0;
char filename[40];
printf("\nEnter memory image file name: ");
gets(filename); /* get filename */
printf("\nLoad Memory Data");
if((fp=fopen(filename,"rb"))!=NULL) //Open file, initialise fp
{
while((addr<MEMSIZE)&&((c=fgetc(fp))!=EOF))
{
cpu.MEM[addr]=c;
if(addr%4==0) printf("\n");
printf("\tMEM[%d]=%d", addr, cpu.MEM[addr]);
addr++;
}
fclose(fp); //Close file
return(1); //tell main to continue
}
else
/* Display messege and return zero if fopen returns NULL. */
{
printf("\nCould not open file \"%s\".\n\n", filename);
return(0); //tell main to stop
}
}
/************************************************************************/
/****** Dump Memory Data ******/
/************************************************************************/
void Dump_Memory_Data()
{
FILE *fp;
unsigned char addr;
/* Create file to store memory and register data. */
if((fp=fopen("CPU_DUMP.txt", "wt"))!=NULL)
{
/* Write memory data to file */
fprintf(fp, "Memory Data\n");
/* loop for each memory location
and write memory data to the file */
for(addr=0;addr<MEMSIZE;addr++)
fprintf(fp, "MEM[%d]=%d\n", addr, cpu.MEM[addr]);
/* Write register data to file */
fprintf(fp, "\nCPU Registers\n");
fprintf(fp, "ir=%d\n", cpu.reg.ir);
fprintf(fp, "acc=%d\n", cpu.reg.acc);
fprintf(fp, "pc=%d\n", cpu.reg.pc);
fprintf(fp, "mar=%d\n", cpu.reg.mar);
fprintf(fp, "mdr=%d\n", cpu.reg.mdr);
/* write status register flags to file */
fprintf(fp, "\nStatus Register\n");
fprintf(fp, "unf=%d\n", cpu.reg.unf);
fprintf(fp, "ovf=%d\n", cpu.reg.ovf);
fprintf(fp, "crf=%d", cpu.reg.crf);
fclose(fp); //close file
}
else /* If fopen failed */
{
printf("Cannot create file \"CPU_Data.txt\".\n\n");
}
}
/************************************************************************/
/****** Initialise CPU ******/
/************************************************************************/
void Initialise_CPU(void)
{
/* Set registers to zero and display result. */
printf("\n\nInitialise CPU:");
cpu.reg.acc = 0;
printf("\tACC=%d, ", cpu.reg.acc);
cpu.reg.pc = 0;
printf("\tPC=%d, ", cpu.reg.pc);
cpu.reg.unf = 0;
printf("\tUNF=%d, ", cpu.reg.unf);
cpu.reg.ovf = 0;
printf("\tOVF=%d, ", cpu.reg.ovf);
cpu.reg.crf = 0;
printf("\tCRF=%d.\n", cpu.reg.crf);
}
/************************************************************************/
/****** Fetch ******/
/************************************************************************/
void Fetch(void)
{
/* copy pc to mar, then increment pc */
cpu.reg.mar = cpu.reg.pc;
cpu.reg.pc++;
/* copy memory data to MDR, then from MDR to IR */
cpu.reg.mdr = cpu.MEM[cpu.reg.mar];
cpu.reg.ir = cpu.reg.mdr;
}
/************************************************************************/
/****** Decode ******/
/************************************************************************/
void Decode(unsigned char *opcode, unsigned char *operand)
{
/* shift right 4 bits bits e.g. 10011101->00001001 */
*opcode = cpu.reg.ir>>4;
/* mask first 4 bits e.g. 10011101->00001101 */
*operand = cpu.reg.ir&15;
}
/************************************************************************/
/****** Execute ******/
/************************************************************************/
void Execute(unsigned char instruction, unsigned char address, unsigned char *fdeloop)
{
cpu.reg.ir = instruction; /* copy instruction to instruction register */
cpu.reg.mar = address; /* copy address to memory address register */
switch(instruction)
{
case LDA: /* copy MEM->mdr->acc */
cpu.reg.mdr=cpu.MEM[cpu.reg.mar];
cpu.reg.acc=cpu.reg.mdr;
/* Reset flags */
cpu.reg.unf=0;
cpu.reg.ovf=0;
cpu.reg.crf=0;
printf("LDA %d", address);
break;
case STA: /* copy acc->mdr->MEM */
cpu.reg.mdr=cpu.reg.acc;
cpu.MEM[cpu.reg.mar]=cpu.reg.mdr;
/* reset flags */
cpu.reg.unf=0;
cpu.reg.ovf=0;
cpu.reg.crf=0;
printf("STA %d", address);
break;
case BRK: /* stop fdeloop in main */
*fdeloop=0;
printf("BRK");
break;
case JMP: /* copy mar->pc */
cpu.reg.pc=cpu.reg.mar;
printf("JMP %d", address);
break;
case BRA: /* if carry flag is set,
copy memory address to program counter */
if(cpu.reg.crf)
cpu.reg.pc=cpu.reg.mar;
printf("BRA %d", address);
break;
case ADD: /* copy mem->mdr */
cpu.reg.mdr=cpu.MEM[cpu.reg.mar];
/* test result for overflow */
if(cpu.reg.acc+cpu.reg.mdr>255)
cpu.reg.ovf=1;
/* add mdr to acc */
cpu.reg.acc+=cpu.reg.mdr;
/* reset flags */
cpu.reg.unf=0;
cpu.reg.crf=0;
printf("ADD %d", address);
break;
case SUB: /* copy mem->mdr */
cpu.reg.mdr=cpu.MEM[cpu.reg.mar];
if(cpu.reg.acc-cpu.reg.mdr<0)
cpu.reg.unf=1;
cpu.reg.acc-=cpu.reg.mdr;
/* reset flags */
cpu.reg.ovf=0;
cpu.reg.crf=0;
printf("SUB %d", address);
break;
case MUL: /* copy mem->mdr */
cpu.reg.mdr=cpu.MEM[cpu.reg.mar];
/* set overflow if result>255 */
if(cpu.reg.acc*cpu.reg.mdr>255)
cpu.reg.ovf=1;
/* multiply acc by mdr */
cpu.reg.acc*=cpu.reg.mdr;
/* reset flags */
cpu.reg.unf=0;
cpu.reg.crf=0;
printf("MUL %d", address);
break;
case DIV: /* copy mem->mdr */
cpu.reg.mdr=cpu.MEM[cpu.reg.mar];
/* set carry if result has remainder */
if(cpu.reg.acc%cpu.reg.mdr!=0)
cpu.reg.crf=1;
/* divide acc by mrd */
cpu.reg.acc/=cpu.reg.mdr;
/* reset flags */
cpu.reg.unf=0;
cpu.reg.ovf=0;
printf("DIV %d", address);
break;
case CLA: /* clear accumulator */
cpu.reg.acc=0;
/* reset flags */
cpu.reg.unf=0;
cpu.reg.ovf=0;
cpu.reg.crf=0;
printf("CLA");
break;
case CLC: /* clear carry flag */
cpu.reg.crf=0;
printf("CLC");
break;
case DMP: /* stop fdeloop in main */
*fdeloop=0;
Dump_Memory_Data();
printf("DMP");
break;
case AND: /* copy mem->mdr */
cpu.reg.mdr=cpu.MEM[cpu.reg.mar];
/* AND acc with mdr */
cpu.reg.acc=cpu.reg.acc&cpu.reg.mdr;
/* reset flags */
cpu.reg.unf=0;
cpu.reg.ovf=0;
cpu.reg.crf=0;
printf("AND %d", address);
break;
case DEC: /* set unf if result<0 */
if(cpu.reg.acc-1<0) cpu.reg.unf=1;
/* perform decrement acc*/
cpu.reg.acc--;
/* reset flags */
cpu.reg.ovf=0;
cpu.reg.crf=0;
printf("DEC");
break;
case BRO: /* copy mar->pc, if ovf is set */
if(cpu.reg.ovf) cpu.reg.pc=cpu.reg.mar;
printf("BRO %d", address);
break;
case BRU: /* copy mar->pc, if unf is set */
if(cpu.reg.unf) cpu.reg.pc=cpu.reg.mar;
printf("BRU %d", address);
break;
default: printf("Invalid instruction!"); //in case of error
}
}
/************************************************************************/
/****** Display Registers ******/
/************************************************************************/
void Display_Registers(unsigned char start)
{
if(start)
printf("\n\t| IR\tACC\tPC\tMAR\tMDR\tUNF\tOVF\tCRF\n");
else
{
printf("\t| %d",cpu.reg.ir);
printf("\t%d",cpu.reg.acc);
printf("\t%d",cpu.reg.pc);
printf("\t%d",cpu.reg.mar);
printf("\t%d",cpu.reg.mdr);
printf("\t%d",cpu.reg.unf);
printf("\t%d",cpu.reg.ovf);
printf("\t%d\n",cpu.reg.crf);
}
}