Skip to content

Commit

Permalink
added transpiled javascript
Browse files Browse the repository at this point in the history
  • Loading branch information
darkzense committed Nov 20, 2023
1 parent c1cc3ab commit 59d4e94
Show file tree
Hide file tree
Showing 16 changed files with 749 additions and 3 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# Transpiled javascript
build

# Logs
logs
*.log
Expand Down
99 changes: 99 additions & 0 deletions build/assembler.js
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 };
57 changes: 57 additions & 0 deletions build/cpu/arch.js
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 };
109 changes: 109 additions & 0 deletions build/cpu/components.js
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;
56 changes: 56 additions & 0 deletions build/cpu/execution_unit.js
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 };
7 changes: 7 additions & 0 deletions build/cpu/interface.js
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;
}
}
53 changes: 53 additions & 0 deletions build/edit_actions.js
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());
Loading

0 comments on commit 59d4e94

Please sign in to comment.