Skip to content

Commit

Permalink
Merge pull request #3 from tcorzo/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
tcorzo authored Nov 2, 2024
2 parents 0a147ed + 8bf2e9b commit 926d4ba
Show file tree
Hide file tree
Showing 23 changed files with 1,198 additions and 162 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ a template for your own programs.
## Roadmap

- [x] Implement basic functionality
- [ ] Add breakpoints
- [ ] Show errors in the UI
- [ ] Separate Data registers from Instruction registers
- [x] Add breakpoints
- [x] Show errors in the UI
- [x] Separate Data registers from Instruction registers
- [x] Timeout for infinite loops
- [x] Add tests
- [ ] Export program to CSV
- [ ] Indicate current register in the UI
- [x] Indicate current register in the UI
- [ ] Flash register when it changes
- [ ] Add CI checks
4 changes: 3 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
"importMap": "./import_map.json"
"imports": {
"@/": "./src/"
}
}
674 changes: 673 additions & 1 deletion deno.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": { "gh-pages": "^6.2.0", "vue": "^3.5.10" },
"dependencies": {
"gh-pages": "^6.2.0",
"vue": "^3.5.10"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"typescript": "^5.5.3",
Expand Down
42 changes: 12 additions & 30 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,38 +1,22 @@
<script setup lang="ts">
import { provide } from 'vue';
import { globalState, resetProgram, toggleMode } from './state';
import { globalState, toggleMode } from './state';
import AuxTable from './components/AuxTable.vue';
import OperationsTable from './components/OperationsTable.vue';
import ProgramTable from './components/ProgramTable.vue';
import Emulator from './components/Emulator.vue';
import UploadProgramButton from './components/UploadProgramButton.vue';
import Editor from './components/Editor.vue';
provide('globalState', globalState);
</script>

<template>
<div id="app">
<div id="editor">
<div class="tables-container">
<AuxTable class="aux-table"/>
<OperationsTable class="operations-table"/>
</div>
<ProgramTable class="program-table"/>
<Emulator v-if="globalState.mode === 'run'"></Emulator>
</div>



<UploadProgramButton v-if="globalState.mode === 'edit'"></UploadProgramButton>

<button @click="resetProgram" class="reset-button" v-if="globalState.mode === 'edit'">
Reset Program 🔄
</button>

<button @click="toggleMode" class="mode-toggle">
{{ globalState.mode === 'edit' ? 'Ejecutar ▶️' : 'Editar ✏️' }}
</button>

<Editor v-if="globalState.mode === 'edit'"></Editor>
<Emulator v-if="globalState.mode === 'run'"></Emulator>
</div>
</template>

Expand All @@ -41,12 +25,6 @@ provide('globalState', globalState);
padding: 20px;
}
#editor {
display: flex;
flex-direction: row;
align-items: top;
}
.mode-toggle {
margin-bottom: 20px;
padding: 10px 20px;
Expand All @@ -62,7 +40,9 @@ provide('globalState', globalState);
margin-bottom: 20px;
}
.aux-table, .operations-table, .program-table {
.aux-table,
.operations-table,
.program-table {
flex: 1;
margin: 0 10px;
}
Expand All @@ -71,7 +51,9 @@ table {
width: 100%;
border-collapse: collapse;
}
th, td {
th,
td {
border: 1px solid black;
padding: 8px;
text-align: center;
Expand Down
62 changes: 51 additions & 11 deletions src/abacus/abacus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ export default class AbacusEmulator {
private _accumulator: string = '0000'; // 4 bytes
private _current_address: string = '000'; // 3 bytes for the address
public registers: Map<string, Register> = new Map(); // 3 bytes for the address
public _breakpoints: string[] = [];
private _breakpoints: string[] = [];
public createdAddresses: string[] = [];
public finished: boolean = false;
public error: string = '';

public timeout = 3000; // 3 seconds in milliseconds

constructor() { }

Expand All @@ -34,17 +39,37 @@ export default class AbacusEmulator {

public getRegister(address: string): Register {
let register = this.registers.get(address);

if (!register) {
register = new Register({ address: address, value: '0000', comment: '' });
this.registers.set(address, register);
this.createdAddresses.push(address);
}
return register;

return this.registers.get(address)!;
}

public setRegister(address: string, register: Register): void {
if (!this.getRegister(address))
this.createdAddresses.push(address);

this.registers.set(address, register);
}


/**
* Checks if a register exists at the specified address.
*
* @param address - The memory address to check for a register.
* @returns `true` if a register exists at the specified address, `false` otherwise.
*/
public checkRegister(address: string): boolean {
if (!this.registers.get(address))
return false;

return true;
}

public addBreakpoint(address: string): void {
this._breakpoints.push(address);
}
Expand All @@ -68,9 +93,15 @@ export default class AbacusEmulator {
this.accumulator = '0000';
this.current_address = program.registers[0].address;
this.registers = new Map(program.registers.map(r => [r.address, r]));
this.error = '';
this.finished = false;

for (const register of program.aux_registers) {
this.registers.set(register.address, register);
}

for (const auxRegister of program.aux_registers) {
this.setRegister(auxRegister.address, auxRegister);
for (const register of program.data_registers) {
this.registers.set(register.address, register);
}

for (const operation of program.operations) {
Expand All @@ -83,14 +114,19 @@ export default class AbacusEmulator {
throw new Error('No program loaded');
}

const startTime = Date.now();
const currentTimeout = this.timeout;

// Continue after current breakpoint
if (this.hasBreakpoint(this.current_address))
this.step();

while (true) {
// End of program
if (this.current_address === '000')
break;
while (!this.finished) {
// Check timeout
if (Date.now() - startTime > currentTimeout) {
this.error = `Program execution timeout ${currentTimeout}ms`;
return;
}

// Breakpoint
if (this.hasBreakpoint(this.current_address))
Expand All @@ -104,19 +140,23 @@ export default class AbacusEmulator {
if (!this.program)
throw new Error('No program loaded');

if (this.current_address === '000')
if (!this.checkRegister(this.current_address)) {
this.error = `Unknown address: ${this.current_address}`;
return;
}

const operation = this.operations.get(this.current_register.opcode);

if (!operation) {
console.error(`Unknown operation code: ${this.current_register.opcode} at address ${this.current_address}`);
throw new Error("Unknown operation code");
this.error = `Unknown operation code: ${this.current_register.opcode} at address ${this.current_address}`;
return;
}

const old_address = this.current_address;

operation.execute.call(this);
if (this.finished)
return;

// Don't increment address if the operation is a jump
if (old_address === this.current_address)
Expand Down
23 changes: 17 additions & 6 deletions src/abacus/importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ export class ProgramImporter {
static importFromCSV(content: string): Program {
const lines = content.split('\n').map(line => line.trim()).filter(line => line.length > 0);

let section: 'OPERATIONS' | 'AUX' | 'PROGRAM' | null = null;
let section: 'OPERATIONS' | 'AUX' | 'PROGRAM' | 'DATA' | null = null;
const operations: Operation[] = [];
const auxRegisters: Register[] = [];
const registers: Register[] = [];
const dataRegisters: Register[] = [];

for (const line of lines) {
const [col1, col2, col3] = line.split(',').map(col => col.trim());

if (col1 === 'OPERATIONS' || col1 === 'AUX' || col1 === 'PROGRAM') {
section = col1 as 'OPERATIONS' | 'AUX' | 'PROGRAM';
if (col1 === 'OPERATIONS' || col1 === 'AUX' || col1 === 'PROGRAM' || col1 === 'DATA') {
section = col1 as 'OPERATIONS' | 'AUX' | 'PROGRAM' | 'DATA';
continue;
}

Expand Down Expand Up @@ -51,15 +52,25 @@ export class ProgramImporter {
}));
break;
}
case 'DATA': {
if (!col1) continue;
dataRegisters.push(new Register({
address: col1,
value: col2 || '0000',
comment: col3 || ''
}));
break;
}
}
}

return {
return new Program({
name: 'imported_program',
description: '',
operations,
aux_registers: auxRegisters,
registers
};
registers,
data_registers: dataRegisters
});
}
}
2 changes: 1 addition & 1 deletion src/abacus/operation_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const END: OperationType = {
id: 'END',
name: 'Fin de Program',
execute: function (this: AbacusEmulator) {
this.current_address = '000';
this.finished = true;
}
};

Expand Down
35 changes: 34 additions & 1 deletion src/abacus/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,45 @@ export class Register {
return this.value.slice(-3);
}

public clone(): Register {
return new Register({ address: this.address, value: this.value, comment: this.comment });
}

}

export interface Program {
export class Program {
name: string;
description: string;
operations: Operation[];
aux_registers: Register[];
registers: Register[];
data_registers: Register[];

constructor({ name, description, operations, aux_registers, registers, data_registers }: {
name: string;
description: string;
operations: Operation[];
aux_registers: Register[];
registers: Register[];
data_registers: Register[];
}) {
this.name = name;
this.description = description;
this.operations = operations;
this.aux_registers = aux_registers;
this.registers = registers;
this.data_registers = data_registers;
}

public clone(): Program {
return new Program({
name: this.name,
description: this.description,
operations: this.operations.map(op => ({ code: op.code, operation_type: op.operation_type })),
aux_registers: this.aux_registers.map(reg => reg.clone()),
registers: this.registers.map(reg => reg.clone()),
data_registers: this.data_registers.map(reg => reg.clone())
});
}

}
38 changes: 38 additions & 0 deletions src/components/Editor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup lang="ts">
import { inject, } from 'vue';
import UploadProgramButton from './UploadProgramButton.vue';
import { provide } from 'vue';
import EditAuxTable from './editor/EditAuxTable.vue';
import OperationsTable from './editor/EditOperationsTable.vue';
import ProgramTable from './editor/EditProgramTable.vue';
import { globalState, resetProgram } from './../state';
provide('globalState', inject('globalState'));
</script>

<template>
<div id="editor">
<div class="tables-container">
<EditAuxTable class="aux-table" />
<OperationsTable class="operations-table" />
</div>
<ProgramTable class="program-table" />

<UploadProgramButton></UploadProgramButton>


<button @click="resetProgram" v-if="globalState.mode === 'edit'">
Reset Program 🔄
</button>
</div>
</template>

<style scoped>
#editor {
display: flex;
flex-direction: row;
align-items: flex-start;
}
</style>
Loading

0 comments on commit 926d4ba

Please sign in to comment.