-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
749 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,3 @@ | ||
# Transpiled javascript | ||
build | ||
|
||
# Logs | ||
logs | ||
*.log | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { instructionSet, syntax } from "./cpu/arch.js"; | ||
import { getDataByVariable } from "./memory/data.js"; | ||
import { getSourceCode } from "./editor.js"; | ||
import { Instruction } from "./cpu/interface.js"; | ||
// Palabra de 16 bits | ||
const OPCODE_LENGTH = 4; | ||
// const ADRESSING_MODE_LENGTH = 1; | ||
const PARAM_LENGTH = 11; | ||
function parseInstruction(text) { | ||
if (!text) { | ||
return null; | ||
} | ||
let mnemonic = text.split(" ")[0].toUpperCase(); | ||
const operation = instructionSet[mnemonic]; | ||
if (!operation) { | ||
throw new ReferenceError(`The operation "${mnemonic}" doesn't exists.`); | ||
} | ||
let syntaxChecker = new RegExp(syntax[operation.type], "g"); | ||
const instructionItems = syntaxChecker.exec(text); | ||
if (!instructionItems) { | ||
throw new SyntaxError(`Couldn't parse "${text}". Check the syntax.`); | ||
} | ||
const operationCode = instructionSet[mnemonic].code; | ||
let addressingMode = 0; | ||
let operandValue = 0; | ||
if (instructionItems.length < 3) { | ||
// Doesn't have operands | ||
return new Instruction(operationCode, addressingMode, operandValue); | ||
} | ||
const operand = instructionItems[2]; | ||
const operandData = getDataByVariable("main-data", operand); | ||
if (operandData) { | ||
addressingMode = 1; | ||
if (!operandData.value) { | ||
throw new ReferenceError(`The symbol "${operand}" is not initialized.`); | ||
} | ||
operandValue = operandData.address.parseHex(); | ||
} | ||
else { | ||
operandValue = +operand; | ||
if (isNaN(operandValue)) { | ||
throw new ReferenceError(`The symbol "${operand}" is not defined.`); | ||
} | ||
} | ||
return new Instruction(operationCode, addressingMode, operandValue); | ||
} | ||
function encodeInstruction(content) { | ||
const ins = parseInstruction(content); | ||
if (!ins) { | ||
return ""; | ||
} | ||
const code = ins.code.toString(2).padStart(OPCODE_LENGTH, "0"); | ||
const addressMode = ins.addressingMode.toString(2); | ||
const operand = ins.operand.toString(2).padStart(PARAM_LENGTH, "0"); | ||
return code + addressMode + operand; | ||
} | ||
function decodeInstruction(content) { | ||
const operationCode = content.substring(0, 4).parseBin(); | ||
const addressingMode = content.substring(4, 5).parseBin(); | ||
const operand = content.substring(5).parseBin(); | ||
return new Instruction(operationCode, addressingMode, operand); | ||
} | ||
// Use for development only | ||
function decodeText(binaryInstruction) { | ||
const ins = decodeInstruction(binaryInstruction); | ||
let opName = ""; | ||
for (const operation in instructionSet) { | ||
const defaultInstruction = instructionSet[operation]; | ||
if (defaultInstruction.code == ins.code) { | ||
opName = operation; | ||
} | ||
} | ||
if (!opName) { | ||
throw new SyntaxError("Couldn't parse Operation Code"); | ||
} | ||
return `${opName} ${ins.operand}`; | ||
} | ||
function makeObjectCode() { | ||
const objCode = []; | ||
const sourceCode = getSourceCode(); | ||
// Check that "END" is present and at the beginning of a line | ||
// with or without additional spaces. | ||
if (!/\n\s*end/.test(sourceCode.toLocaleLowerCase())) { | ||
throw new SyntaxError('Missing "END" instruction.'); | ||
} | ||
for (const line of sourceCode.split("\n")) { | ||
const trimmedLine = line.trim(); | ||
// Ignore entire comment lines | ||
if (trimmedLine.startsWith(";")) { | ||
objCode.push(""); | ||
} | ||
else { | ||
const objSrc = encodeInstruction(trimmedLine); | ||
objCode.push(objSrc); | ||
} | ||
} | ||
return objCode; | ||
} | ||
export { makeObjectCode, decodeInstruction }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { Push, Pop } from "../instructions/stack.js"; | ||
import { Add, Sub, Mul, Div } from "../instructions/arithmetic.js"; | ||
import { End } from "../instructions/process.js"; | ||
import { Input, Output } from "../instructions/io.js"; | ||
const syntax = { | ||
addressable: /([a-zA-Z]+)\s+(\d+)/, | ||
op: /([a-zA-Z]+)/, | ||
transfer: /([a-zA-Z]+)\s+(\d+|[a-zA-Z_][a-zA-Z0-9_]*)/, | ||
}; | ||
const instructionSet = { | ||
PUSH: { | ||
code: 0, | ||
type: "transfer", | ||
executor: new Push(), | ||
}, | ||
POP: { | ||
code: 1, | ||
type: "transfer", | ||
executor: new Pop(), | ||
}, | ||
ADD: { | ||
code: 2, | ||
type: "op", | ||
executor: new Add(), | ||
}, | ||
SUB: { | ||
code: 3, | ||
type: "op", | ||
executor: new Sub(), | ||
}, | ||
MUL: { | ||
code: 4, | ||
type: "op", | ||
executor: new Mul(), | ||
}, | ||
DIV: { | ||
code: 5, | ||
type: "op", | ||
executor: new Div(), | ||
}, | ||
END: { | ||
code: 6, | ||
type: "op", | ||
executor: new End(), | ||
}, | ||
IN: { | ||
code: 7, | ||
type: "addressable", | ||
executor: new Input(), | ||
}, | ||
OUT: { | ||
code: 8, | ||
type: "addressable", | ||
executor: new Output(), | ||
}, | ||
}; | ||
export { instructionSet, syntax }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
const MAR = document.getElementById("input-mar"); | ||
const MDR = document.getElementById("input-mdr"); | ||
const instructionRegister = (document.getElementById("input-ir")); | ||
const stackPointer = document.getElementById("input-sp"); | ||
const programCounter = document.getElementById("input-pc"); | ||
const aluFlags = document.getElementById("input-flags"); | ||
const aluX = document.getElementById("input-alu-x"); | ||
const aluY = document.getElementById("input-alu-y"); | ||
const aluZ = document.getElementById("input-alu-z"); | ||
const controlUnit = (document.getElementById("txtarea-cunit")); | ||
const port0 = document.getElementById("input-port0"); | ||
const inputSwitch = document.getElementById("input-switch"); | ||
const port1 = document.getElementById("input-port1"); | ||
const led = document.getElementById("img-led"); | ||
class CpuHardware { | ||
get MAR() { | ||
return MAR.value; | ||
} | ||
set MAR(value) { | ||
MAR.value = value; | ||
} | ||
get MDR() { | ||
return MDR.value; | ||
} | ||
set MDR(value) { | ||
MDR.value = value; | ||
} | ||
get instructionRegister() { | ||
return instructionRegister.value; | ||
} | ||
set instructionRegister(value) { | ||
instructionRegister.value = value; | ||
} | ||
get stackPointer() { | ||
return stackPointer.value; | ||
} | ||
set stackPointer(value) { | ||
stackPointer.value = value; | ||
} | ||
get programCounter() { | ||
return programCounter.value; | ||
} | ||
set programCounter(value) { | ||
programCounter.value = value; | ||
} | ||
get aluFlags() { | ||
return aluFlags.value; | ||
} | ||
set aluFlags(value) { | ||
aluFlags.value = value; | ||
} | ||
get aluX() { | ||
return aluX.value; | ||
} | ||
set aluX(value) { | ||
aluX.value = value; | ||
} | ||
get aluY() { | ||
return aluY.value; | ||
} | ||
set aluY(value) { | ||
aluY.value = value; | ||
} | ||
get aluZ() { | ||
return aluZ.value; | ||
} | ||
set aluZ(value) { | ||
aluZ.value = value; | ||
} | ||
get controlUnit() { | ||
return controlUnit.value; | ||
} | ||
set controlUnit(value) { | ||
controlUnit.value = value; | ||
} | ||
readPort(n) { | ||
if (n == 0) { | ||
return +port0.value; | ||
} | ||
else { | ||
return +port1.value; | ||
} | ||
} | ||
writePort(n, value) { | ||
if (n == 0) { | ||
port0.value = value.asBin8(); | ||
} | ||
else { | ||
port1.value = value.asBin8(); | ||
port1.dispatchEvent(new Event("change")); | ||
} | ||
} | ||
} | ||
const cpu = new CpuHardware(); | ||
inputSwitch.addEventListener("change", () => { | ||
// Port 0 toggler | ||
cpu.writePort(0, +!cpu.readPort(0)); | ||
}); | ||
port1.addEventListener("change", () => { | ||
const port1Value = cpu.readPort(1); | ||
if (port1Value % 2) { | ||
// LSB=1 | ||
led.src = "img/led-on.png"; | ||
} | ||
else { | ||
led.src = "img/led-off.png"; | ||
} | ||
}); | ||
export default cpu; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import cpu from "./components.js"; | ||
import { clearStackData, processInfo } from "../memory/data.js"; | ||
import { decodeInstruction } from "../assembler.js"; | ||
import { instructionSet } from "./arch.js"; | ||
import { resetSpOffset } from "../instructions/stack.js"; | ||
function fetch() { | ||
cpu.MAR = cpu.programCounter; | ||
const counter = cpu.programCounter.parseHex(); | ||
if (counter == processInfo.objectCode.length) { | ||
processInfo.deallocate(); | ||
return null; | ||
} | ||
cpu.aluX = cpu.programCounter; | ||
cpu.aluY = "0000"; | ||
cpu.aluFlags = "Z=0,O=0"; | ||
cpu.controlUnit = "PCout,MARin,ClrY,SetCin,ADD,Zin"; | ||
const nextCounterHex = (counter + 1).asHex16(); | ||
cpu.programCounter = cpu.aluZ = nextCounterHex; | ||
const binInstruction = processInfo.objectCode[counter]; | ||
if (!binInstruction) { | ||
return null; | ||
} | ||
const hexInstruction = binInstruction.parseBin().asHex16(); | ||
cpu.instructionRegister = cpu.MDR = hexInstruction; | ||
cpu.programCounter = (counter + 1).asHex16(); | ||
return decodeInstruction(binInstruction); | ||
} | ||
function getExecutor(opCode) { | ||
for (const operation in instructionSet) { | ||
const builtinInstruction = instructionSet[operation]; | ||
if (builtinInstruction.code == opCode) { | ||
if (!builtinInstruction.executor) { | ||
throw new ReferenceError(`The operation "${operation}" is not yet implemented.`); | ||
} | ||
return builtinInstruction.executor; | ||
} | ||
} | ||
throw new ReferenceError(`The operation "${opCode}" doesn't exists.`); | ||
} | ||
function resetRegisters() { | ||
const hexRegisters = (document.getElementsByClassName("hex-reg")); | ||
for (const reg of hexRegisters) { | ||
reg.value = "0000"; | ||
} | ||
const textRegisters = (document.getElementsByClassName("text-reg")); | ||
for (const reg of textRegisters) { | ||
reg.value = ""; | ||
} | ||
resetSpOffset(); | ||
clearStackData(); | ||
} | ||
function execute(instruction) { | ||
const runner = getExecutor(instruction.code); | ||
runner.run(instruction); | ||
} | ||
export { fetch, execute, resetRegisters }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export class Instruction { | ||
constructor(code, addressMode, operand) { | ||
this.code = code; | ||
this.addressingMode = addressMode; | ||
this.operand = operand; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { getSourceCode, setSourceCode } from "./editor.js"; | ||
import { makeObjectCode } from "./assembler.js"; | ||
const SRC_OUTPUT_NAME = "code.asm"; | ||
const OBJ_OUTPUT_NAME = "bincode.txt"; | ||
const srcFileSelector = (document.getElementById("srcfile-selector")); | ||
const btnUploadSrc = (document.getElementById("btn-upload-src")); | ||
const btnDownloadSrc = (document.getElementById("btn-download-src")); | ||
const btnDownloadObj = (document.getElementById("btn-download-obj")); | ||
function download(filename, text) { | ||
let element = document.createElement("a"); | ||
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text)); | ||
element.setAttribute("download", filename); | ||
element.style.display = "none"; | ||
document.body.appendChild(element); | ||
element.click(); | ||
document.body.removeChild(element); | ||
} | ||
function downloadSourceCode() { | ||
download(SRC_OUTPUT_NAME, getSourceCode()); | ||
} | ||
function downloadObjCode() { | ||
try { | ||
download(OBJ_OUTPUT_NAME, makeObjectCode().join("\n")); | ||
} | ||
catch (e) { | ||
alert(e.message); | ||
} | ||
} | ||
function readTextFile(file) { | ||
// Check if the file is an image. | ||
if (file.type && !file.type.startsWith("text/")) { | ||
alert(`The file "${file.name}" is not a text file.`); | ||
return; | ||
} | ||
const reader = new FileReader(); | ||
reader.addEventListener("load", (event) => { | ||
if (event.target) { | ||
let fileContent = event.target.result; | ||
setSourceCode(fileContent); | ||
} | ||
}); | ||
reader.readAsText(file); | ||
} | ||
function loadSourceCode(event) { | ||
const fileList = event.target.files; | ||
if (fileList && fileList.length > 0) { | ||
readTextFile(fileList[0]); | ||
} | ||
} | ||
btnDownloadSrc.addEventListener("click", downloadSourceCode); | ||
btnDownloadObj.addEventListener("click", downloadObjCode); | ||
srcFileSelector.addEventListener("change", loadSourceCode); | ||
btnUploadSrc.addEventListener("click", () => srcFileSelector.click()); |
Oops, something went wrong.