Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
<button type="button" class="btn btn-default navbar-btn" ng-click="executeStep()" ng-disabled="isRunning"><span class="glyphicon glyphicon-forward"></span> Step</button>
</div>
<button type="button" class="btn btn-default navbar-btn" ng-click="reset()">Reset</button>
<input type="checkbox" id="autoAssembleCheckBox" checked>Auto Assemble</input>
<button type="button" class="btn btn-default navbar-btn" ng-click="save()">Save</button>
<input style="display: inline;" type="file" id="fileLoadInput" class="btn btn-default navbar-btn" onchange="angular.element(this).scope().load(this.files)" />
</div>
<div class="navbar-header navbar-right">
<a class="navbar-brand" style="color:#FFFFFF">Simple 8-bit Assembler Simulator</a>
Expand Down Expand Up @@ -82,15 +85,15 @@ <h4 class="panel-title">CPU & Memory</h4>
</thead>
<tbody>
<tr style="text-align:center;" class="source-code">
<td><div style="margin:auto;" ng-class="displayA && 'marker marker-a'"><small>{{ cpu.gpr[0] | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" ng-class="displayB && 'marker marker-b'"><small>{{ cpu.gpr[1] | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" ng-class="displayC && 'marker marker-c'"><small>{{ cpu.gpr[2] | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" ng-class="displayD && 'marker marker-d'"><small>{{ cpu.gpr[3] | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" class="marker marker-ip"><small>{{ cpu.ip | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" class="marker marker-sp"><small>{{ cpu.sp | number:displayHex }}</small></div></td>
<td><small>{{ cpu.zero | flag }}</small></td>
<td><small>{{ cpu.carry | flag }}</small></td>
<td><small>{{ cpu.fault | flag }}</small></td>
<td><div style="margin:auto;" ng-class="displayA && 'marker marker-a'" oncontextmenu="angular.element(this).scope().changeRegister(0);return false;"><small>{{ cpu.gpr[0] | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" ng-class="displayB && 'marker marker-b'" oncontextmenu="angular.element(this).scope().changeRegister(1);return false;"><small>{{ cpu.gpr[1] | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" ng-class="displayC && 'marker marker-c'" oncontextmenu="angular.element(this).scope().changeRegister(2);return false;"><small>{{ cpu.gpr[2] | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" ng-class="displayD && 'marker marker-d'" oncontextmenu="angular.element(this).scope().changeRegister(3);return false;"><small>{{ cpu.gpr[3] | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" class="marker marker-ip" oncontextmenu="angular.element(this).scope().changeRegister(128);return false;"><small>{{ cpu.ip | number:displayHex }}</small></div></td>
<td><div style="margin:auto;" class="marker marker-sp" oncontextmenu="angular.element(this).scope().changeRegister(129);return false;"><small>{{ cpu.sp | number:displayHex }}</small></div></td>
<td oncontextmenu="angular.element(this).scope().changeRegister(130);return false;"><small>{{ cpu.zero | flag }}</small></td>
<td oncontextmenu="angular.element(this).scope().changeRegister(131);return false;"><small>{{ cpu.carry | flag }}</small></td>
<td oncontextmenu="angular.element(this).scope().changeRegister(132);return false;"><small>{{ cpu.fault | flag }}</small></td>
</tr>
</tbody>
</table>
Expand All @@ -99,7 +102,7 @@ <h4 class="panel-title">CPU & Memory</h4>
<div class="memory-block"
ng-repeat="m in memory.data track by $index"
ng-class="getMemoryCellCss($index)">
<div ng-class="getMemoryInnerCellCss($index)" ng-switch="isInstruction($index)">
<div ng-class="getMemoryInnerCellCss($index)" ng-switch="isInstruction($index)" ng-right-click="changeMemory($index)">
<small ng-switch-default>{{ m | number:displayHex }}</small>
<a ng-switch-when="true" ng-click="jumpToLine($index)">
<small>{{ m | number:displayHex }}</small>
Expand Down Expand Up @@ -161,7 +164,7 @@ <h4 class="panel-title">Labels</h4>
</div>
</div>
<hr style="margin-top:10px;margin-bottom:10px;"/>
<p><small>by Marco Schweighauser (2015) | MIT License | <a href="https://www.mschweighauser.com/make-your-own-assembler-simulator-in-javascript-part1/" target="_blank">Blog</a></small></p>
<p><small>by Marco Schweighauser (2015) | MIT License | <a href="https://www.mschweighauser.com/make-your-own-assembler-simulator-in-javascript-part1/" target="_blank">Blog</a> | modified by Filip Schauer (2019)</small></p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.10/angular.min.js"></script>
<script src="assets/asmsimulator.js"></script>
Expand Down
32 changes: 21 additions & 11 deletions src/assembler/asm.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
app.service('assembler', ['opcodes', function (opcodes) {
function isNotEmpty(el) {return el.length !== 0;}

return {
go: function (input) {
// Use https://www.debuggex.com/
Expand Down Expand Up @@ -204,17 +206,25 @@ app.service('assembler', ['opcodes', function (opcodes) {

switch (instr) {
case 'DB':
p1 = getValue(match[op1_group]);

if (p1.type === "number")
code.push(p1.value);
else if (p1.type === "numbers")
for (var j = 0, k = p1.value.length; j < k; j++) {
code.push(p1.value[j]);
}
else
throw "DB does not support this operand";

var dbline = lines[i];
var dbtemp = dbline.indexOf(":");
var dbtemp2 = dbline.indexOf(";");
dbline = dbline.substring((dbtemp == -1) ? 0 : (dbtemp+1), (dbtemp2 == -1) ? (dbline.length) : dbtemp2);
var dblinesplit = dbline.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g);
var dbcount = 0;
while(dblinesplit[dbcount] !== undefined){
p1 = getValue(dblinesplit[dbcount]);

if (p1.type === "number")
code.push(p1.value);
else if (p1.type === "numbers")
for (var j = 0, k = p1.value.length; j < k; j++) {
code.push(p1.value[j]);
}
else
throw "DB does not support this operand";
++dbcount;
}
break;
case 'HLT':
checkNoExtraArg('HLT', match[op1_group]);
Expand Down
134 changes: 129 additions & 5 deletions src/ui/controller.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
app.directive('ngRightClick', function($parse) {
return function(scope, element, attrs) {
var fn = $parse(attrs.ngRightClick);
element.bind('contextmenu', function(event) {
scope.$apply(function() {
event.preventDefault();
fn(scope, {$event:event});
});
});
};
});

app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'assembler', function ($document, $scope, $timeout, cpu, memory, assembler) {
$scope.memory = memory;
$scope.cpu = cpu;
Expand All @@ -12,12 +24,124 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
$scope.speeds = [{speed: 1, desc: "1 HZ"},
{speed: 4, desc: "4 HZ"},
{speed: 8, desc: "8 HZ"},
{speed: 16, desc: "16 HZ"}];
$scope.speed = 4;
{speed: 16, desc: "16 HZ"},
{speed: 32, desc: "32 HZ"},
{speed: 64, desc: "64 HZ"}];
$scope.speed = 8;
$scope.outputStartIndex = 232;

$scope.code = "; Simple example\n; Writes Hello World to the output\n\n JMP start\nhello: DB \"Hello World!\" ; Variable\n DB 0 ; String terminator\n\nstart:\n MOV C, hello ; Point to var \n MOV D, 232 ; Point to output\n CALL print\n HLT ; Stop execution\n\nprint: ; print(C:*from, D:*to)\n PUSH A\n PUSH B\n MOV B, 0\n.loop:\n MOV A, [C] ; Get char from var\n MOV [D], A ; Write to output\n INC C\n INC D \n CMP B, [C] ; Check if end\n JNZ .loop ; jump if not\n\n POP B\n POP A\n RET";
$scope.code = "; Simple example\n; Writes Hello World to the output\n\n JMP start\nhello: DB \"Hello World!\", 0 ; Variable\n\nstart:\n MOV C, hello ; Point to var \n MOV D, 232 ; Point to output\n CALL print\n HLT ; Stop execution\n\nprint: ; print(C:*from, D:*to)\n PUSH A\n PUSH B\n MOV B, 0\n.loop:\n MOV A, [C] ; Get char from var\n MOV [D], A ; Write to output\n INC C\n INC D\n CMP B, [C] ; Check if end\n JNZ .loop ; jump if not\n\n POP B\n POP A\n RET";

function toHexString(byteArray) {
return Array.from(byteArray, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
}

function hexStringToByte(str) {
if (!str) {
return new Uint8Array();
}

var a = [];
for (var i = 0, len = str.length; i < len; i += 2) {
a.push(parseInt(str.substr(i,2),16));
}

return new Uint8Array(a);
}

function hexStringToWord(str) {
if (!str) {
return new Uint16Array();
}

var a = [];
for (var i = 0, len = str.length; i < len; i += 4) {
a.push(parseInt(str.substr(i,4),16));
}

return new Uint16Array(a);
}

$scope.changeRegister = function (regIndex) {
var value = prompt("Please type in a hex value (1 for TRUE / 0 for FALSE)");
if(value !== null && value !== ""){
var val = parseInt(value, 16);
if(!isNaN(val)) {
if((val >= 0) && (val <= 0xFF)){
if(regIndex == 0x80){
cpu.ip = val;
} else if(regIndex == 0x81){
cpu.sp = val;
} else if(regIndex == 0x82){
cpu.zero = (val >= 1);
} else if(regIndex == 0x83){
cpu.carry = (val >= 1);
} else if(regIndex == 0x84){
cpu.fault = (val >= 1);
} else {
cpu.gpr[regIndex] = val;
}
$scope.$apply();
}
}
}
return false;
};

$scope.changeMemory = function (memIndex) {
var value = prompt("Please type in a hex value");
if(value !== null && value !== ""){
var val = parseInt(value, 16);
if(!isNaN(val)) {
if((val >= 0) && (val <= 0xFF)){
$scope.memory.data[memIndex] = val;
//$scope.$apply();
}
}
}
return false;
};

$scope.save = function () {
var hexString = toHexString($scope.memory.data);
for(var i = 0; i < 256; ++i) {
hexString = hexString + ('000' + ($scope.mapping[i] & 0xFFFF).toString(16)).slice(-4);
}
hexString = hexString + $scope.code;
var uriContent = "data:application/octet-stream," + encodeURIComponent(hexString);
var newWindow = window.open(uriContent, 'neuesDokument');
};

$scope.load = function (files) {
var f = files[0];
if(f){
var reader = new FileReader();
reader.onload = function(e) {

$scope.reset();
var contents = e.target.result;
var hexString = contents.substr(0, 512);
var hexArr = hexStringToByte(hexString);
var mappingString = contents.substr(512, 1536);
var mappingArr = hexStringToWord(mappingString);
if($scope.mapping === undefined) $scope.mapping = {};
for(var i = 0; i < 256; ++i){
$scope.memory.data[i] = hexArr[i];
$scope.mapping[i] = mappingArr[i];
}
$scope.code = contents.substr(1536, f.size-1536);
$scope.$apply();
//$document[0].getElementById('sourceCode').value = $scope.code;
//$scope.assemble();
};
reader.readAsText(f);
} else {
alert("No file found");
}
};

$scope.reset = function () {
cpu.reset();
memory.reset();
Expand All @@ -26,7 +150,7 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass
};

$scope.executeStep = function () {
if (!$scope.checkPrgrmLoaded()) {
if (!$scope.checkPrgrmLoaded() && document.getElementById("autoAssembleCheckBox").checked) {
$scope.assemble();
}

Expand All @@ -48,7 +172,7 @@ app.controller('Ctrl', ['$document', '$scope', '$timeout', 'cpu', 'memory', 'ass

var runner;
$scope.run = function () {
if (!$scope.checkPrgrmLoaded()) {
if (!$scope.checkPrgrmLoaded() && document.getElementById("autoAssembleCheckBox").checked) {
$scope.assemble();
}

Expand Down