First Commit

This commit is contained in:
MatCat 2021-04-03 19:03:47 -07:00
commit f116826575
4 changed files with 416 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea/

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# MatCat 8SA1 CPU Simulator
This is a simple JS based simulator to simulate the function of the 8SA1 CPU.

8
index.html Normal file
View File

@ -0,0 +1,8 @@
<html>
<head>
<title>MatCat's 8SA1 CPU Simulator</title>
</head>
<body>
<script src="js/main.js"></script>
</body>
</html>

404
js/main.js Normal file
View File

@ -0,0 +1,404 @@
// Setup a few bitmasks we can use in the code that are handy to clean inputs to registers
const BITMASK_8 = 0x00000000000000ff;
const BITMASK_16 = 0x000000000000ffff;
const BITMASK_24 = 0x0000000000ffffff;
// These are the control lines that are controlled by the microcode RAM output
const CONTROL_PCC = 0b00000000000000000000000000000001; // PC CLK UP
const CONTROL_PCI = 0b00000000000000000000000000000010; // PC Input Enable
const CONTROL_SPD = 0b00000000000000000000000000000100; // SP Count Down
const CONTROL_SPC = 0b00000000000000000000000000001000; // SP CLK (UP / DOWN)
const CONTROL_SPI = 0b00000000000000000000000000010000; // SP Input Enable
const CONTROL_RAIL = 0b00000000000000000000000000100000; // GPA LOW Input Enable
const CONTROL_RAIH = 0b00000000000000000000000001000000; // GPA HIGH Input Enable
const CONTROL_RBIL = 0b00000000000000000000000010000000; // GPB LOW Input Enable
const CONTROL_RBIH = 0b00000000000000000000000100000000; // GPB HIGH Input Enable
const CONTROL_RCIL = 0b00000000000000000000001000000000; // GPC LOW Input Enable
const CONTROL_RCIH = 0b00000000000000000000010000000000; // GPC HIGH Input Enable
const CONTROL_RDIL = 0b00000000000000000000100000000000; // GPD LOW Input Enable
const CONTROL_RDIH = 0b00000000000000000001000000000000; // GPD HIGH Input Enable
const CONTROL_RRI = 0b00000000000000000010000000000000; // LOW Ram Register Input Enable
const CONTROL_RHI = 0b00000000000000000100000000000000; // HIGH Ram Register Input Enable
const CONTROL_ALUM0 = 0b00000000000000001000000000000000; // ALU MUX 0
const CONTROL_ALUM1 = 0b00000000000000010000000000000000; // ALU MUX 1
const CONTROL_ALUI = 0b00000000000000100000000000000000; // ALU Invert (high side)
const CONTROL_ALUC = 0b00000000000001000000000000000000; // ALU Carry Input
const CONTROL_ALUSL = 0b00000000000010000000000000000000; // ALU Shift Left
const CONTROL_ALUSR = 0b00000000000100000000000000000000; // ALU Shift Right
const CONTROL_OEM0 = 0b00000000001000000000000000000000; // Output Enable MUX 0
const CONTROL_OEM1 = 0b00000000010000000000000000000000; // Output Enable MUX 1
const CONTROL_OEM2 = 0b00000000100000000000000000000000; // Output Enable MUX 2
const CONTROL_OEM3 = 0b00000001000000000000000000000000; // Output Enable MUX 3
const CONTROL_OEM4 = 0b00000010000000000000000000000000; // Output Enable MUX 4
const CONTROL_OEME = 0b00000100000000000000000000000000; // Output Enable MUX Enable
const CONTROL_RI = 0b00010000000000000000000000000000; // RAM Input Enable
const CONTROL_IRI = 0b00100000000000000000000000000000; // Instruction Register Input Enable
const CONTROL_MCL0 = 0b01000000000000000000000000000000; // Microcode Counter to 0
const CONTROL_MCL8 = 0b1000000000000000000000000000000; // Microcode Counter to 8
// These are the output enable control lines, they are muxed since none of them can ever be on together
const OECONTROL_PC = 0b00000 // PC Register
const OECONTROL_SP = 0b00001 // SP Register
const OECONTROL_AL = 0b00010 // GPA to LOW
const OECONTROL_AH = 0b00011 // GPA to HIGH
const OECONTROL_BL = 0b00100 // GPB to LOW
const OECONTROL_BH = 0b00101 // GPB to HIGH
const OECONTROL_CL = 0b00110 // GPC to LOW
const OECONTROL_CH = 0b00111 // GPC to HIGH
const OECONTROL_DL = 0b01000 // GPD to LOW
const OECONTROL_DH = 0b01001 // GPD to HIGH
const OECONTROL_AB = 0b01010 // GPB to HIGH, GPA to LOW
const OECONTROL_AC = 0b01011 // GPC to HIGH, GPA to LOW
const OECONTROL_AD = 0b01100 // GPD to HIGH, GPA to LOW
const OECONTROL_BA = 0b01101 // GPA to HIGH, GPB to LOW
const OECONTROL_BC = 0b01110 // GPC to HIGH, GPB to LOW
const OECONTROL_BD = 0b01111 // GPD to HIGH, GPB to LOW
const OECONTROL_CA = 0b10000 // GPA to HIGH, GPC to LOW
const OECONTROL_CB = 0b10001 // GPB to HIGH, GPC to LOW
const OECONTROL_CD = 0b10010 // GPD to HIGH, GPC to LOW
const OECONTROL_DA = 0b10011 // GPA to HIGH, GPD to LOW
const OECONTROL_DB = 0b10100 // GPB to HIGH, GPD to LOW
const OECONTROL_DC = 0b10101 // GPC to HIGH, GPD to LOW
const OECONTROL_AE = 0b11100 // ALU Enable
const OECONTROL_AO = 0b11101 // ALU Output to LOW
const OECONTROL_SR = 0b11110 // Status Register to LOW
const OECONTROL_RO = 0b11111 // RAM to DATABUS Enable
class CPU_8SA1 {
constructor() {
this.DATABUS = 0;
this.ADDRBUS = 0;
this.PC = 0; // Set PC register to 0
this.SP = BITMASK_16; // Set SP register to 0xFFFF
this.SR = 0; // Set the STATUS register
this.RR = 0; // Set lower RAM register to 0
this.RH = 0; // Set higher RAM register to 0
this.GPA = 0; // Set the 4 GP registers
this.GPB = 0;
this.GPC = 0;
this.GPD = 0;
this.MCC = 0; // Set the microcode counter
this.IR = 0; // Set the initial IR
this.MC_Controls = 0; // Set the control output lines
this.ALUSUM = 0; // ALU Register to hold SUM
this.MCRAM = new Array(2 ** 16); // Initialize the microcode RAM
this.RAM = new Array(2 ** 24); // Initialize the main RAM array
this._CLK = false;
}
CLOCK(clk_val) {
if (clk_val && !this._CLK) {
// Clock going HIGH
this._CLK = true;
this._CLOCK_HIGH();
} else {
if (!clk_val && this._CLK) {
// Clock going LOW
this._CLK = false;
this._CLOCK_LOW();
}
}
}
_CLOCK_HIGH() {
// Call anything that needs to be called on positive clock edge
if (this.MC_Controls & CONTROL_PCC ) this.PC_CLK();
if (this.MC_Controls & CONTROL_PCI ) this.PC_In_CLK();
if (this.MC_Controls & CONTROL_SPC ) this.SP_CLK();
if (this.MC_Controls & CONTROL_SPI ) this.SP_In_CLK();
if (this.MC_Controls & CONTROL_RI ) this.RAM_In_CLK();
if (this.MC_Controls & CONTROL_IRI ) this.IR_In_CLK();
if (this.MC_Controls & CONTROL_RRI ) this.RR_In_CLK();
if (this.MC_Controls & CONTROL_RHI ) this.RH_In_CLK();
if (this.MC_Controls & CONTROL_RAIL) this.GPA_In_LOW_CLK();
if (this.MC_Controls & CONTROL_RAIH) this.GPA_In_HIGH_CLK();
if (this.MC_Controls & CONTROL_RBIL) this.GPB_In_LOW_CLK();
if (this.MC_Controls & CONTROL_RBIH) this.GPB_In_HIGH_CLK();
if (this.MC_Controls & CONTROL_RCIL) this.GPC_In_LOW_CLK();
if (this.MC_Controls & CONTROL_RCIH) this.GPC_In_HIGH_CLK();
if (this.MC_Controls & CONTROL_RDIL) this.GPD_In_LOW_CLK();
if (this.MC_Controls & CONTROL_RDIH) this.GPD_In_HIGH_CLK();
}
_CLOCK_LOW() {
// Call anything that needs to be called on low going clock edge
this._FetchDecode_CLK_neg();
}
_FetchDecode_CLK_neg() {
// We actually setup all of our fetch and decode logic during the clocks low phase
this.MCC += 1;
let MC_Controls = this.MCRAM[((this.IR & 0b0000001111111111) << 4) | this.MCC | ((this.SR & 0b00000011) << 14)];
if (MC_Controls & CONTROL_MCL0) this.MCC = 0;
if (MC_Controls & CONTROL_MCL8) this.MCC = 8;
if (MC_Controls !== this.MC_Controls) {
if (this.MC_Controls & CONTROL_OEME) {
// Set the DATABUS based on whatever output we are controlling
let OUTMUX = (this.MC_Controls & CONTROL_OEM4) ? 0b10000 : 0;
OUTMUX |= (this.MC_Controls & CONTROL_OEM3) ? 0b01000 : 0;
OUTMUX |= (this.MC_Controls & CONTROL_OEM2) ? 0b00100 : 0;
OUTMUX |= (this.MC_Controls & CONTROL_OEM1) ? 0b00010 : 0;
OUTMUX |= (this.MC_Controls & CONTROL_OEM0) ? 0b00001 : 0;
switch (OUTMUX) {
case OECONTROL_PC: {
this.DATABUS = this.PC;
break;
}
case OECONTROL_SP: {
this.DATABUS = this.SP;
break;
}
case OECONTROL_AL: {
this.DATABUS = ((this.DATABUS & BITMASK_8) << 8) | (this.GPA & BITMASK_8);
break;
}
case OECONTROL_AH: {
this.DATABUS = ((this.GPA & BITMASK_8) << 8) | (this.DATABUS & BITMASK_8);
break;
}
case OECONTROL_BL: {
this.DATABUS = ((this.DATABUS & BITMASK_8) << 8) | (this.GPB & BITMASK_8);
break;
}
case OECONTROL_BH: {
this.DATABUS = ((this.GPB & BITMASK_8) << 8) | (this.DATABUS & BITMASK_8);
break;
}
case OECONTROL_CL: {
this.DATABUS = ((this.DATABUS & BITMASK_8) << 8) | (this.GPC & BITMASK_8);
break;
}
case OECONTROL_CH: {
this.DATABUS = ((this.GPC & BITMASK_8) << 8) | (this.DATABUS & BITMASK_8);
break;
}
case OECONTROL_DL: {
this.DATABUS = ((this.DATABUS & BITMASK_8) << 8) | (this.GPD & BITMASK_8);
break;
}
case OECONTROL_DH: {
this.DATABUS = ((this.GPD & BITMASK_8) << 8) | (this.DATABUS & BITMASK_8);
break;
}
case OECONTROL_AB: {
this.DATABUS = ((this.GPB & BITMASK_8) << 8) | (this.GPA & BITMASK_8);
break;
}
case OECONTROL_AC: {
this.DATABUS = ((this.GPC & BITMASK_8) << 8) | (this.GPA & BITMASK_8);
break;
}
case OECONTROL_AD: {
this.DATABUS = ((this.GPD & BITMASK_8) << 8) | (this.GPA & BITMASK_8);
break;
}
case OECONTROL_BA: {
this.DATABUS = ((this.GPA & BITMASK_8) << 8) | (this.GPB & BITMASK_8);
break;
}
case OECONTROL_BC: {
this.DATABUS = ((this.GPC & BITMASK_8) << 8) | (this.GPB & BITMASK_8);
break;
}
case OECONTROL_BD: {
this.DATABUS = ((this.GPD & BITMASK_8) << 8) | (this.GPB & BITMASK_8);
break;
}
case OECONTROL_CA: {
this.DATABUS = ((this.GPA & BITMASK_8) << 8) | (this.GPC & BITMASK_8);
break;
}
case OECONTROL_CB: {
this.DATABUS = ((this.GPB & BITMASK_8) << 8) | (this.GPC & BITMASK_8);
break;
}
case OECONTROL_CD: {
this.DATABUS = ((this.GPD & BITMASK_8) << 8) | (this.GPC & BITMASK_8);
break;
}
case OECONTROL_DA: {
this.DATABUS = ((this.GPA & BITMASK_8) << 8) | (this.GPD & BITMASK_8);
break;
}
case OECONTROL_DB: {
this.DATABUS = ((this.GPB & BITMASK_8) << 8) | (this.GPD & BITMASK_8);
break;
}
case OECONTROL_DC: {
this.DATABUS = ((this.GPC & BITMASK_8) << 8) | (this.GPD & BITMASK_8);
break;
}
case OECONTROL_AE: {
this.DATABUS = (this.GPB >> 8) | this.GPA;
let ALU_A = this.DATABUS & BITMASK_8;
let ALU_B = (this.DATABUS & (BITMASK_8 << 8)) >> 8;
let ALU_Result = 0;
let ALUMUX = (MC_Controls & CONTROL_ALUM1) ? 0b10 : 0;
ALUMUX |= (MC_Controls & CONTROL_ALUM0) ? 0b01 : 0;
let SHIFTING = false;
if (MC_Controls & CONTROL_ALUSL) SHIFTING = true;
if (MC_Controls & CONTROL_ALUSR) SHIFTING = true;
switch (ALUMUX) {
case 0: { // ADD
if (CONTROL_ALUI) ALU_B = ~ALU_B;
ALU_Result = ALU_A + ALU_B;
if (MC_Controls & CONTROL_ALUC && !SHIFTING) ALU_Result += 1;
break;
}
case 1: { // AND
ALU_Result = ALU_A & ALU_B;
if (CONTROL_ALUI) ALU_Result = ~ALU_Result;
if (MC_Controls & CONTROL_ALUC && !SHIFTING) ALU_Result += 1;
break;
}
case 2: { // OR
ALU_Result = ALU_A | ALU_B;
if (CONTROL_ALUI) ALU_Result = ~ALU_Result;
if (MC_Controls & CONTROL_ALUC && !SHIFTING) ALU_Result += 1;
break;
}
case 3: { // XOR
ALU_Result = ALU_A ^ ALU_B;
if (CONTROL_ALUI) ALU_Result = ~ALU_Result;
if (MC_Controls & CONTROL_ALUC && !SHIFTING) ALU_Result += 1;
break;
}
}
if (MC_Controls & CONTROL_ALUSL) ALU_Result = ALU_Result << 1;
if ((MC_Controls & CONTROL_ALUSL) && (MC_Controls & CONTROL_ALUC)) ALU_Result = ALU_Result | 0b00000001;
if (MC_Controls & CONTROL_ALUSR) ALU_Result = ALU_Result >> 1;
if ((MC_Controls & CONTROL_ALUSR) && (MC_Controls & CONTROL_ALUC)) ALU_Result = ALU_Result | 0b10000000;
if (ALU_Result & 0b100000000) {
// We have a carry
this.SR = this.SR | 0b00000001;
} else {
this.SR = this.SR & 0b11111110;
}
if (ALU_Result === 0) {
// We have a ZERO
this.SR = this.SR | 0b00000010;
} else {
this.SR = this.SR & 0b11111101;
}
this.ALUSUM = ALU_Result & BITMASK_8;
break;
}
case OECONTROL_AO: {
this.DATABUS = (this.DATABUS & (BITMASK_8 << 8)) | this.ALUSUM;
break;
}
case OECONTROL_SR: {
this.DATABUS = ((this.DATABUS & BITMASK_8) << 8) | (this.SR & BITMASK_8);
break;
}
case OECONTROL_RO: {
this.DATABUS = this.RAM[this.ADDRBUS];
break;
}
}
}
}
this.MC_Controls = MC_Controls;
}
_FetchDecode_CLK_pos() {
}
PC_CLK() {
this.PC += 1;
if (this.PC > BITMASK_16) this.PC = 0;
}
PC_In_CLK() {
this.PC = this.DATABUS;
}
SP_CLK() {
if (this.MC_Controls & CONTROL_SPD) {
this.SP -= 1;
if (this.SP < 0) this.SP = BITMASK_16;
} else {
this.SP += 1;
if (this.SP > BITMASK_16) this.SP = 0;
}
}
SP_In_CLK() {
this.SP = this.DATABUS;
}
RAM_In_CLK() {
this.RAM[this.ADDRBUS] = this.DATABUS;
}
IR_In_CLK() {
this.IR = this.DATABUS;
}
RR_In_CLK() {
this.RR = this.DATABUS;
}
RH_In_CLK() {
this.RH = this.DATABUS;
}
GPA_In_LOW_CLK() {
this.GPA = this.DATABUS & 0x00ff;
}
GPA_In_HIGH_CLK() {
this.GPA = (this.DATABUS & 0xff00) >> 8;
}
GPB_In_LOW_CLK() {
this.GPB = this.DATABUS & 0x00ff;
}
GPB_In_HIGH_CLK() {
this.GPB = (this.DATABUS & 0xff00) >> 8;
}
GPC_In_LOW_CLK() {
this.GPC = this.DATABUS & 0x00ff;
}
GPC_In_HIGH_CLK() {
this.GPC = (this.DATABUS & 0xff00) >> 8;
}
GPD_In_LOW_CLK() {
this.GPD = this.DATABUS & 0x00ff;
}
GPD_In_HIGH_CLK() {
this.GPD = (this.DATABUS & 0xff00) >> 8;
}
PrintDebug() {
// We want to print out all the registers in a way that makes sense
}
}