Skip to content

Commit

Permalink
Merge pull request #16 from yaacov/add-nop-and-arg-length-table
Browse files Browse the repository at this point in the history
Add NOP command and a list for command lengths
  • Loading branch information
yaacov authored Oct 2, 2023
2 parents 29caab5 + 98f3be8 commit 335f632
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 57 deletions.
1 change: 1 addition & 0 deletions README.ASM.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SMART Assembly is a simple assembly language designed for educational purposes.

Here's a list of available opcodes in the SMART Assembly language:

- `NOP`: No operation, move the program counter.
- `LOADA <label>`: Load the value from the memory location specified by `<label>` into register A.
- `LOADB <label>`: Load the value from the memory location specified by `<label>` into register B.
- `STOREA <label>`: Store the value from register A into the memory location specified by `<label>`.
Expand Down
2 changes: 1 addition & 1 deletion public/js/components/code-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class CodeEditor extends HTMLElement {
highlightText() {
const text = this.editor.innerHTML;
const highlightedText = text
.replace(/(LOADA|LOADB|STOREA|STOREB|ORA|ORB|ANDA|ANDB|XORA|XORB|NOTA|NOTB|SHLA|SHLB|SHRA|SHRB|JUMP|JZA|JZB|ADDA|ADDB|END|DATA)/g, '<span class="code">$1</span>')
.replace(/(NOP|LOADA|LOADB|STOREA|STOREB|ORA|ORB|ANDA|ANDB|XORA|XORB|NOTA|NOTB|SHLA|SHLB|SHRA|SHRB|JUMP|JZA|JZB|ADDA|ADDB|END|DATA)/g, '<span class="code">$1</span>')
.replace(/(0x[\da-fA-F]{2})/g, '<span class="literal">$1</span>')
.replace(/(^|<br>)(\s*\w+:)/g, '$1<span class="label">$2</span>')
.replace(/(;.*?)(<br>|$)/gm, (_, p1, p2) => {
Expand Down
28 changes: 13 additions & 15 deletions src/compiler.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import opcodes from './opcodes.mjs';
import {
findKeyByValue, formatAddress, formatHexByte, formatBinary,
} from './utils.mjs';
import { opcodes, opcodesParams } from './opcodes.mjs';
import { findKeyByValue } from './utils.mjs';
import { throwFormattedError } from './debug.mjs';

/**
* Removes comments from a line of assembly code.
Expand Down Expand Up @@ -62,7 +61,7 @@ function processLabel(instruction, operands, labels, address) {

if (instruction === 'DATA') {
address += operands.length; // For each data value
} else if (opcodes[instruction] !== undefined) {
} else if (instruction in opcodes) {
address += 1; // For the opcode
address += operands.length; // For each operand
}
Expand All @@ -80,11 +79,12 @@ function processLabel(instruction, operands, labels, address) {
function processInstruction(instruction, operands, memory, labels, memoryMapping) {
const addToMemory = (value, type, asmOpcode = null, asmOperand = null) => {
const address = memory.length;
const valueInt = parseInt(value, 10);

memory.push(value);
memory.push(valueInt);
memoryMapping.push({
address,
value: parseInt(value, 10),
value: valueInt,
type,
asmOpcode,
asmOperand,
Expand All @@ -96,22 +96,20 @@ function processInstruction(instruction, operands, memory, labels, memoryMapping
operands.forEach((operand) => {
addToMemory(parseValue(operand), 'data', null, operand);
});
} else if (opcodes[instruction] !== undefined) {
} else if (instruction in opcodes) {
addToMemory(opcodes[instruction], 'opcode', instruction);

if (operands.length !== opcodesParams[opcodes[instruction]]) {
throwFormattedError('Wrong number of operands', instruction, memory.length);
}

operands.forEach((operand) => {
const operandValue = findKeyByValue(labels, operand) || parseValue(operand);
addToMemory(operandValue, 'operand', null, operand);
});
} else if (instruction) {
// Note: a valid line with a lable and no opCode will have an instruction == undefined.
const formattedInstructionHex = formatHexByte(instruction);
const formattedInstructionBinary = formatBinary(instruction, 8); // Assuming 8 bits for the instruction
const formattedAddressHex = formatAddress(memory.length);
const formattedAddressDecimal = memory.length;

const errorMessage = `Invalid instruction [${formattedInstructionHex} | ${formattedInstructionBinary}] at address ${formattedAddressHex} (${formattedAddressDecimal})!`;
throw new Error(errorMessage);
throwFormattedError('Invalid instruction', instruction, memory.length);
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/debug.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ import {
formatHexByte, formatBinary, formatDecimal, formatAddress,
} from './utils.mjs';

/**
* Formats the error message and throws an error.
*
* @param {string} message - The error message.
* @param {number} instruction - The instruction code.
* @param {number} memoryLength - The length of the memory.
* @throws {Error} Throws an error with a formatted message.
*/
export function throwFormattedError(message, instruction, memoryLength) {
const formattedAddressHex = formatAddress(memoryLength);
const formattedAddressDecimal = memoryLength;

const errorMessage = `${message} opCOde [${instruction}] at address [${formattedAddressHex} (${formattedAddressDecimal})]`;
throw new Error(errorMessage);
}

/**
* Dumps the labels and their addresses to the console.
*
Expand Down
9 changes: 6 additions & 3 deletions src/disassemble.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import opcodes from './opcodes.mjs';
import { opcodes } from './opcodes.mjs';
import { findKeyByValue } from './utils.mjs';

/**
Expand Down Expand Up @@ -58,8 +58,6 @@ export function disassemble(memory) {
case 'LOADB':
case 'STOREA':
case 'STOREB':
case 'ORA':
case 'ORB':
case 'ANDA':
case 'ANDB':
case 'XORA':
Expand All @@ -86,6 +84,11 @@ export function disassemble(memory) {
operandValue = memory[++address];
asmOperand = operandValue.toString(); // Directly use the integer value for shifts
break;
case 'ORA':
case 'ORB':
case 'NOP':
// obCodes with no operands
break;
case 'END':
progEnd = true;
break;
Expand Down
71 changes: 49 additions & 22 deletions src/opcodes.mjs
Original file line number Diff line number Diff line change
@@ -1,26 +1,53 @@
const opcodes = {
LOADA: 0x00,
LOADB: 0x01,
STOREA: 0x02,
STOREB: 0x03,
ORA: 0x04,
ORB: 0x05,
ANDA: 0x06,
ANDB: 0x07,
XORA: 0x08,
XORB: 0x09,
NOTA: 0x0A,
NOTB: 0x0B,
SHLA: 0x0C,
SHLB: 0x0D,
SHRA: 0x0E,
SHRB: 0x0F,
JUMP: 0x10,
JZA: 0x11,
JZB: 0x12,
ADDA: 0x13,
ADDB: 0x14,
export const opcodes = {
NOP: 0x00,
LOADA: 0x01,
LOADB: 0x02,
STOREA: 0x03,
STOREB: 0x04,
ORA: 0x05,
ORB: 0x06,
ANDA: 0x07,
ANDB: 0x08,
XORA: 0x09,
XORB: 0x0A,
NOTA: 0x0B,
NOTB: 0x0C,
SHLA: 0x0D,
SHLB: 0x0E,
SHRA: 0x0F,
SHRB: 0x10,
JUMP: 0x11,
JZA: 0x12,
JZB: 0x13,
ADDA: 0x14,
ADDB: 0x15,
END: 0xFF,
};

export const opcodesParams = {
[opcodes.NOP]: 0,
[opcodes.LOADA]: 1,
[opcodes.LOADB]: 1,
[opcodes.STOREA]: 1,
[opcodes.STOREB]: 1,
[opcodes.ORA]: 1,
[opcodes.ORB]: 1,
[opcodes.ANDA]: 1,
[opcodes.ANDB]: 1,
[opcodes.XORA]: 1,
[opcodes.XORB]: 1,
[opcodes.NOTA]: 0,
[opcodes.NOTB]: 0,
[opcodes.SHLA]: 1,
[opcodes.SHLB]: 2,
[opcodes.SHRA]: 1,
[opcodes.SHRB]: 1,
[opcodes.JUMP]: 1,
[opcodes.JZA]: 1,
[opcodes.JZB]: 1,
[opcodes.ADDA]: 1,
[opcodes.ADDB]: 1,
[opcodes.END]: 0,
};

export default opcodes;
24 changes: 8 additions & 16 deletions src/vm.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import opcodes from './opcodes.mjs';
import {
formatHexByte, formatBinary, formatAddress,
} from './utils.mjs';
import { opcodes, opcodesParams } from './opcodes.mjs';
import { throwFormattedError } from './debug.mjs';

class VM {
constructor(memorySize = 256) {
Expand All @@ -12,8 +10,7 @@ class VM {
}

fetchOperand() {
this.pc++;
return this.memory[this.pc];
return this.memory[this.pc + 1];
}

/**
Expand All @@ -23,8 +20,10 @@ class VM {
*/
execute() {
let jumpFlag = false;
const instruction = this.memory[this.pc];

const handlers = {
[opcodes.NOP]: () => true,
[opcodes.LOADA]: () => this.registerA = this.memory[this.fetchOperand()],
[opcodes.LOADB]: () => this.registerB = this.memory[this.fetchOperand()],
[opcodes.STOREA]: () => this.memory[this.fetchOperand()] = this.registerA,
Expand All @@ -49,22 +48,15 @@ class VM {
[opcodes.END]: () => false,
};

const instruction = this.memory[this.pc];
if (handlers[instruction]) {
if (instruction in handlers) {
handlers[instruction]();
} else {
const formattedInstructionHex = formatHexByte(instruction);
const formattedInstructionBinary = formatBinary(instruction, 8); // Assuming 8 bits for the instruction
const formattedAddressHex = formatAddress(this.pc);
const formattedAddressDecimal = this.pc;

const errorMessage = `Invalid instruction [${formattedInstructionHex} | ${formattedInstructionBinary}] at address ${formattedAddressHex} (${formattedAddressDecimal})!`;
throw new Error(errorMessage);
throwFormattedError('Invalid instruction', instruction, this.pc);
}

// Dont incriment the counter on jumps
if (!jumpFlag) {
this.pc++;
this.pc += (1 + opcodesParams[instruction]);
}

// Memory gurd
Expand Down

0 comments on commit 335f632

Please sign in to comment.