orca-assembler
Version:
1,321 lines (1,210 loc) • 59 kB
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>orca</title>
<script src="https://unpkg.com/show-open-file-picker@0.2.2/polyfill.js"></script>
<style>
* {
box-sizing: border-box;
font-family: inherit;
}
/* element styles */
a, a:visited {
text-decoration: none;
color: #0078d4;
}
button, .cm-search .cm-button {
padding: 3px 5px;
font-size: 14px;
background: #0078d4;
color: #ffffff;
border: 1px solid #ffffff12;
}
button:hover {
background: #026ec1;
}
button.unavailable {
background: #313131;
color: #cccccc;
}
button.unavailable:hover {
border: 1px solid #3c3c3c;
}
code {
font-family: Consolas, 'Courier New', Courier, monospace;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-text-fill-color: #cccccc;
-webkit-box-shadow: 0 0 0 30px #313131 inset !important;
}
input:invalid {
border: 1px solid #f44747;
}
input:not([type]), input[type=text], input[type=number], .cm-search input:not([type]) {
font-size: 14px;
background: #313131;
color: #cccccc;
border: 1px solid #3c3c3c;
}
:is(input:not([type]), input[type=text], input[type=number], .cm-search input:not([type])):focus {
outline: none;
border: 1px solid #0078d4;
}
input[type=number] {
appearance: textfield;
}
input[type=checkbox] {
background: #313131;
color: #cccccc;
border: 1px solid #3c3c3c;
}
input[type=checkbox]:checked {
background: #202020 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none' viewBox='0 0 16 16'><line x1='2' y1='9' x2='6' y2='13' stroke='white' /><line x1='6' y1='13' x2='14' y2='3' stroke='white' /></svg>");
border: 1px solid #cccccc;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
appearance: none;
margin: 0;
}
label {
font-size: 14px;
}
select {
padding: 0 24px 0 5px;
appearance: none;
background-color: #313131;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' preserveAspectRatio='none' viewBox='0 0 16 16'><line x1='1' y1='6' x2='6' y2='10' stroke='%23aeaeae' /><line x1='11' y1='6' x2='6' y2='10' stroke='%23aeaeae' /></svg>");
background-repeat: no-repeat;
background-position: right;
background-size: 16px;
color: #cccccc;
border: 1px solid #3c3c3c;
}
option {
background-color: #1f1f1f;
}
select:focus {
outline: none;
border: 1px solid #0078d4;
}
table {
border-collapse: collapse;
}
table, tr, th, td {
border: 1px solid #3c3c3c;
}
th, td {
padding: 2px 3px;
}
/* class styles */
.flex-item-right {
margin-left: auto;
}
.bin {
box-sizing: content-box;
width: 8ch;
}
.oct {
box-sizing: content-box;
width: 3ch;
}
.dec {
box-sizing: content-box;
width: 3ch;
}
.hex {
box-sizing: content-box;
width: 2ch;
}
/* syntax highlighting */
.h-comment {
color: #6a9955;
}
.h-invalid {
color: #f44747;
}
.h-variableName {
color: #9cdcfe;
}
.h-number {
color: #b5cea8;
}
.h-keyword {
color: #569cd6;
}
.h-controlKeyword {
color: #c586c0;
}
/* layout */
html {
height: 100%;
overflow: hidden;
}
body {
height: 100%;
margin: 0;
display: flex;
flex-direction: column;
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
background: #1f1f1f;
color: #cccccc;
caret-color: #c6c6c6;
}
header {
flex: 0 0 auto;
}
nav {
display: flex;
flex-direction: row;
align-items: center;
padding: 10px;
gap: 10px;
background: #181818;
}
#docs-button {
width: 108px;
}
#emulator-button {
width: 79px;
}
main {
flex: 1 1 auto;
display: flex;
flex-direction: row;
overflow: hidden;
}
main > div {
width: 50%;
height: 100%;
overflow-y: auto;
}
#editor {
border-right: 1px solid #2b2b2b;
}
.cm-editor span {
background: inherit;
}
.cm-content {
z-index: 2 !important;
}
.cm-selectionBackground, ::selection {
background: #5886ff77 !important;
}
.cm-selectionLayer {
z-index: 1 !important;
}
.cm-selectionMatch {
background: #3a3d41fe !important;
}
.cm-activeLine {
background: unset !important;
}
.cm-gutters, .cm-gutter {
background: #1f1f1f;
color: #6e7681;
}
.cm-activeLineGutter {
color: #cccccc;
}
.cm-panels {
border: 1px solid #2b2b2b;
}
.cm-panels-bottom {
width: 95%;
min-width: 300px;
max-width: 600px;
}
.cm-panels :has(> .cm-search) {
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
border-top: 1px solid #3c3c3c;
border-right: 1px solid #3c3c3c;
}
.cm-search label {
display: inline-flex !important;
flex-direction: row !important;
align-items: center !important;
}
#assembler {
display: flex;
flex-direction: column;
}
#assembler-controls {
padding: 20px 0;
display: flex;
flex-direction: row;
justify-content: space-evenly;
}
#assembled-code {
padding: 10px;
font-family: Consolas, 'Courier New', Courier, monospace;
white-space: pre-wrap;
border-top: 1px solid #2b2b2b;
}
#emulator {
display: none;
padding: 10px;
flex-direction: row;
gap: 10px;
}
#emulator-controls {
display: grid;
grid: repeat(6, max-content) / repeat(4, max-content);
gap: 10px;
}
#start-button {
grid-row: 1;
grid-column: 1;
}
#stop-button {
grid-row: 1;
grid-column: 2;
}
#step-button {
grid-row: 2;
grid-column: 1;
}
#reset-button {
grid-row: 2;
grid-column: 2;
}
#emulator-assemble-button {
grid-row: 1;
grid-column: 3;
}
#speed-label {
grid-row: 3;
grid-column: 1;
}
#speed {
grid-row: 3;
grid-column: 2;
width: 6ch;
}
#registers {
grid-row: 4;
grid-column: 1 / span 4;
display: flex;
flex-direction: column;
}
.register {
display: flex;
flex-direction: row;
font-family: Consolas, 'Courier New', Courier, monospace;
border: 1px solid #3c3c3c;
}
.register > * {
padding: 2px 3px;
}
.register label {
box-sizing: content-box;
width: 3ch;
}
.register input {
background: transparent;
border: none;
border-left: 1px solid #3c3c3c;
}
#clear-data-registers-button {
grid-row: 5;
grid-column: 1 / span 3;
}
div:has(> #emulator-code) {
flex: 1 0 auto;
display: flex;
flex-direction: column;
}
#emulator-code-label {
text-align: center;
padding-bottom: 10px;
}
#emulator-code {
font-family: Consolas, 'Courier New', Courier, monospace;
white-space: pre-wrap;
}
#docs {
display: none;
width: min(800px, 100%);
padding: 0 25px 25px 25px;
tab-size: 4;
color: white;
}
#docs li {
white-space: pre-wrap;
}
#opcode-table th {
box-sizing: content-box;
width: 5ch;
}
</style>
</head>
<body>
<header>
<nav>
<button id="save-button">Save to file</button>
<button id="load-button">Load from file</button>
<button id="docs-button">Documentation</button>
<button id="emulator-button">Emulator</button>
<div class="flex-item-right">
<a href="https://npmjs.com/package/orca-assembler">orca v3.0.0</a>
Licensed under the <a href="https://opensource.org/license/mit">MIT license</a>
</div>
</nav>
</header>
<main>
<div id="editor"></div>
<div id="assembler">
<div id="assembler-controls">
<button id="assemble-button">Assemble</button>
<select id="assembled-code-type">
<option value="hex" selected>Hex</option>
<option value="bin">Binary</option>
<option value="oct">Octal</option>
<option value="dec">Decimal</option>
</select>
</div>
<div id="assembled-code"></div>
</div>
<div id="emulator">
<div id="emulator-controls">
<button id="start-button">Start</button>
<button id="stop-button">Stop</button>
<button id="step-button">Step</button>
<button id="reset-button">Reset</button>
<button id="emulator-assemble-button">Assemble</button>
<label id="speed-label">Speed:</label>
<input type="number" id="speed" value="1" min="1" max="1000" />
<div id="registers">
<div id="register-pc" class="register">
<label>PC: </label>
<input type="text" class="hex" pattern="#[0-9A-Fa-f]{1,2}" value="00" disabled />
<input type="text" class="bin" pattern="#[01]{1,8}" value="00000000" disabled />
<input type="text" class="dec" pattern="#[0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]" value="000" disabled />
<input type="text" class="oct" pattern="#[0-7]{1,3}" value="000" disabled />
</div>
<div id="register-sp" class="register">
<label>SP: </label>
<input type="text" class="hex" pattern="#[0-9A-Fa-f]{1,2}" value="00" disabled />
<input type="text" class="bin" pattern="#[01]{1,8}" value="00000000" disabled />
<input type="text" class="dec" pattern="#[0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]" value="000" disabled />
<input type="text" class="oct" pattern="#[0-7]{1,3}" value="000" disabled />
</div>
<div id="register-ra" class="register">
<label>RA: </label>
<input type="text" class="hex" pattern="#[0-9A-Fa-f]{1,2}" value="00" disabled />
<input type="text" class="bin" pattern="#[01]{1,8}" value="00000000" disabled />
<input type="text" class="dec" pattern="#[0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]" value="000" disabled />
<input type="text" class="oct" pattern="#[0-7]{1,3}" value="000" disabled />
</div>
<div id="register-a" class="register">
<label>A: </label>
<input type="text" class="hex" pattern="#[0-9A-Fa-f]{1,2}" value="00" disabled />
<input type="text" class="bin" pattern="#[01]{1,8}" value="00000000" disabled />
<input type="text" class="dec" pattern="#[0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]" value="000" disabled />
<input type="text" class="oct" pattern="#[0-7]{1,3}" value="000" disabled />
</div>
<div id="register-b" class="register">
<label>B: </label>
<input type="text" class="hex" pattern="#[0-9A-Fa-f]{1,2}" value="00" disabled />
<input type="text" class="bin" pattern="#[01]{1,8}" value="00000000" disabled />
<input type="text" class="dec" pattern="#[0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]" value="000" disabled />
<input type="text" class="oct" pattern="#[0-7]{1,3}" value="000" disabled />
</div>
<div id="register-c" class="register">
<label>C: </label>
<input type="text" class="hex" pattern="#[0-9A-Fa-f]{1,2}" value="00" disabled />
<input type="text" class="bin" pattern="#[01]{1,8}" value="00000000" disabled />
<input type="text" class="dec" pattern="#[0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]" value="000" disabled />
<input type="text" class="oct" pattern="#[0-7]{1,3}" value="000" disabled />
</div>
<div id="register-d" class="register">
<label>D: </label>
<input type="text" class="hex" pattern="#[0-9A-Fa-f]{1,2}" value="00" disabled />
<input type="text" class="bin" pattern="#[01]{1,8}" value="00000000" disabled />
<input type="text" class="dec" pattern="#[0-9]{1,2}|1[0-9]{1,2}|2[0-4][0-9]|25[0-5]" value="000" disabled />
<input type="text" class="oct" pattern="#[0-7]{1,3}" value="000" disabled />
</div>
</div>
<button id="clear-data-registers-button">Clear data registers</button>
</div>
<div>
<div id="emulator-code-label">Code:</div>
<div id="emulator-code"></div>
</div>
</div>
<div id="docs">
<h1>ORCA (Open Redstone Computer Assembler)</h1>
<h2>8-bit mode</h2>
<p>This is the default mode. All instructions set the <code data-h>dest</code> register to the result of the operation (unless otherwise noted) and may have side effects. The inputs to the instruction are <code data-h>x</code> and <code data-h>y</code>.</p>
<h3>Assembled code format</h3>
<table>
<thead>
<tr>
<th><code>0</code></th>
<th><code>1</code></th>
<th><code>2</code></th>
<th><code>3</code></th>
<th><code>4</code></th>
<th><code>5</code></th>
<th><code>6</code></th>
<th><code>7</code></th>
<th><code>8</code></th>
<th><code>9</code></th>
<th><code>A</code></th>
<th><code>B</code></th>
<th><code>C</code></th>
<th><code>D</code></th>
<th><code>E</code></th>
<th><code>F</code></th>
<th>Assembly</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td></td>
<td><code data-h>opcode</code></td>
<td>no arguments</td>
</tr>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td colspan="3"><code data-h>y</code></td>
<td></td>
<td><code data-h>opcode y</code></td>
<td><code data-h>y</code> is a register</td>
</tr>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>1</code></td>
<td colspan="7"><code data-h>y</code></td>
<td><code data-h>opcode y</code></td>
<td><code data-h>y</code> is a value</td>
</tr>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td colspan="3"><code data-h>dest</code></td>
<td><code>1</code></td>
<td colspan="7"><code data-h>y</code></td>
<td><code data-h>opcode dest, y</code></td>
<td><code data-h>dest</code> is a register, <code data-h>y</code> is a value</td>
</tr>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td colspan="3"><code data-h>dest</code></td>
<td><code>0</code></td>
<td colspan="3"><code data-h>dest</code></td>
<td colspan="3"><code data-h>y</code></td>
<td></td>
<td><code data-h>opcode dest, y</code></td>
<td><code data-h>dest</code> and <code data-h>y</code> are registers</td>
</tr>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td colspan="3"><code data-h>dest</code></td>
<td><code>0</code></td>
<td colspan="3"><code data-h>x</code></td>
<td colspan="3"><code data-h>y</code></td>
<td></td>
<td><code data-h>opcode dest, x, y</code></td>
<td><code data-h>dest</code>, <code data-h>x</code>, and <code data-h>y</code> are registers</td>
</tr>
<tr>
<td><code>1</code></td>
<td><code>1</code></td>
<td colspan="3"><code data-h>opcode</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>1</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td colspan="3"><code data-h>y</code></td>
<td></td>
<td><code data-h>opcode y</code></td>
<td><code data-h>y</code> is a register, only if the opcode starts with <code>11</code></td>
</tr>
<tr>
<td><code>1</code></td>
<td><code>1</code></td>
<td colspan="3"><code data-h>opcode</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>1</code></td>
<td><code>1</code></td>
<td colspan="7"><code data-h>y</code></td>
<td><code data-h>opcode y</code></td>
<td><code data-h>y</code> is a value, only if the opcode starts with <code>11</code></td>
</tr>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td><code>0</code></td>
<td><code>0</code></td>
<td><code>1</code></td>
<td><code>0</code></td>
<td colspan="3"><code data-h>x</code></td>
<td colspan="3"><code data-h>y</code></td>
<td></td>
<td><code data-h>opcode x, y</code></td>
<td><code data-h>x</code> and <code data-h>y</code> are registers, only if the opcode starts with <code>11</code></td>
</tr>
</tbody>
</table>
<p>During assembly, if <code data-h>y</code> is a value and more than 127, the last bit of the opcode and <code data-h>y</code> are flipped.</p>
<h3>Registers</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>000</code></td>
<td><code data-h>0</code></td>
<td>Always 0</td>
</tr>
<tr>
<td><code>001</code></td>
<td><code data-h>pc</code></td>
<td>Program counter</td>
</tr>
<tr>
<td><code>010</code></td>
<td><code data-h>sp</code></td>
<td>Stack pointer</td>
</tr>
<tr>
<td><code>011</code></td>
<td><code data-h>ra</code></td>
<td>Return address</td>
</tr>
<tr>
<td><code>100</code></td>
<td><code data-h>a</code></td>
<td>Accumulator</td>
</tr>
<tr>
<td><code>101</code></td>
<td><code data-h>b</code></td>
<td>Backup</td>
</tr>
<tr>
<td><code>110</code></td>
<td><code data-h>c</code></td>
<td>Counter/argument</td>
</tr>
<tr>
<td><code>111</code></td>
<td><code data-h>d</code></td>
<td>Data/temporary</td>
</tr>
</tbody>
</table>
<h3>Machine code format</h3>
<table>
<thead>
<tr>
<th><code>0</code></th>
<th><code>1</code></th>
<th><code>2</code></th>
<th><code>3</code></th>
<th><code>4</code></th>
<th><code>5</code></th>
<th><code>6</code></th>
<th><code>7</code></th>
<th><code>8</code></th>
<th><code>9</code></th>
<th><code>A</code></th>
<th><code>B</code></th>
<th><code>C</code></th>
<th><code>D</code></th>
<th><code>E</code></th>
<th><code>F</code></th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td colspan="3"><code data-h>dest</code></td>
<td><code>1</code></td>
<td colspan="7"><code data-h>y</code></td>
<td><code data-h>x</code> is <code data-h>dest</code></td>
</tr>
<tr>
<td colspan="5"><code data-h>opcode</code></td>
<td colspan="3"><code data-h>dest</code></td>
<td><code>0</code></td>
<td colspan="3"><code data-h>x</code></td>
<td colspan="3"><code data-h>y</code></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h3>Opcode table</h3>
<table id="opcode-table">
<thead>
<tr>
<th></th>
<th><code>00</code></th>
<th><code>01</code></th>
<th><code>10</code></th>
<th><code>11</code></th>
</tr>
</thead>
<tbody>
<tr>
<th><code>000</code></th>
<td><code data-h>nop</code></td>
<td><code data-h>add</code></td>
<td><code data-h>ld</code></td>
<td><code data-h>jez</code></td>
</tr>
<tr>
<th><code>001</code></th>
<td><code data-h>and</code></td>
<td><code data-h>sub</code></td>
<td><code data-h>ldn</code></td>
<td><code data-h>jnz</code></td>
</tr>
<tr>
<th><code>010</code></th>
<td><code data-h>xor</code></td>
<td><code data-h>adc</code></td>
<td><code data-h>st</code></td>
<td><code data-h>jlz</code></td>
</tr>
<tr>
<th><code>011</code></th>
<td><code data-h>or</code></td>
<td><code data-h>sbb</code></td>
<td><code data-h>stn</code></td>
<td><code data-h>jgz</code></td>
</tr>
<tr>
<th><code>100</code></th>
<td><code data-h>mov</code></td>
<td><code data-h>lsh</code></td>
<td><code data-h></code></td>
<td><code data-h>jcf</code></td>
</tr>
<tr>
<th><code>101</code></th>
<td><code data-h>not</code></td>
<td><code data-h>rsh</code></td>
<td><code data-h></code></td>
<td><code data-h>call</code></td>
</tr>
<tr>
<th><code>110</code></th>
<td><code data-h>clc</code></td>
<td><code data-h>arsh</code></td>
<td><code data-h></code></td>
<td><code data-h>swm16</code></td>
</tr>
<tr>
<th><code>111</code></th>
<td><code data-h>stc</code></td>
<td><code data-h></code></td>
<td><code data-h></code></td>
<td><code data-h>stop</code></td>
</tr>
</tbody>
</table>
<h3>Opcode meanings</h3>
<table>
<thead>
<tr>
<th>Opcode</th>
<th>Mnemonic</th>
<th>Result</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>00000</code></td>
<td><code data-h>nop</code></td>
<td><code data-h>0</code></td>
<td></td>
</tr>
<tr>
<td><code>00001</code></td>
<td><code data-h>and</code></td>
<td><code><code data-h>x</code> & <code data-h>y</code></code></td>
<td></td>
</tr>
<tr>
<td><code>00010</code></td>
<td><code data-h>xor</code></td>
<td><code><code data-h>x</code> ^ <code data-h>y</code></code></td>
<td></td>
</tr>
<tr>
<td><code>00011</code></td>
<td><code data-h>or</code></td>
<td><code><code data-h>x</code> | <code data-h>y</code></code></td>
<td></td>
</tr>
<tr>
<td><code>00100</code></td>
<td><code data-h>mov</code></td>
<td><code data-h>y</code></td>
<td></td>
</tr>
<tr>
<td><code>00101</code></td>
<td><code data-h>not</code></td>
<td><code>~<code data-h>y</code></code></td>
<td></td>
</tr>
<tr>
<td><code>00110</code></td>
<td><code data-h>clc</code></td>
<td><code data-h>0</code></td>
<td>Sets the carry flag to 0</td>
</tr>
<tr>
<td><code>00111</code></td>
<td><code data-h>stc</code></td>
<td><code data-h>0</code></td>
<td>Sets the carry flag to 1</td>
</tr>
<tr>
<td><code>01000</code></td>
<td><code data-h>add</code></td>
<td><code><code data-h>x</code> + <code data-h>y</code></code></td>
<td>Sets the carry flag to the carry out</td>
</tr>
<tr>
<td><code>01001</code></td>
<td><code data-h>sub</code></td>
<td><code><code data-h>x</code> - <code data-h>y</code></code></td>
<td>Sets the carry flag to the carry out</td>
</tr>
<tr>
<td><code>01010</code></td>
<td><code data-h>adc</code></td>
<td><code><code data-h>x</code> + <code data-h>y</code> + </code>carry flag</td>
<td>Sets the carry flag to the carry out</td>
</tr>
<tr>
<td><code>01011</code></td>
<td><code data-h>sbb</code></td>
<td><code><code data-h>x</code> - <code data-h>y</code> - </code>carry flag</td>
<td>Sets the carry flag to the carry out</td>
</tr>
<tr>
<td><code>01100</code></td>
<td><code data-h>lsh</code></td>
<td><code><code data-h>y</code> << <code data-h>1</code></code></td>
<td></td>
</tr>
<tr>
<td><code>01101</code></td>
<td><code data-h>rsh</code></td>
<td><code><code data-h>y</code> >> <code data-h>1</code></code></td>
<td></td>
</tr>
<tr>
<td><code>01110</code></td>
<td><code data-h>arsh</code></td>
<td><code><code data-h>y</code> >> <code data-h>1</code></code></td>
<td>Arithmetic, not logical, right shift</td>
</tr>
<tr>
<td><code>10000</code></td>
<td><code data-h>ld</code></td>
<td>The value at address <code data-h>y</code></td>
<td></td>
</tr>
<tr>
<td><code>10001</code></td>
<td><code data-h>ldn</code></td>
<td>The value at address <code>~<code data-h>y</code></code></td>
<td></td>
</tr>
<tr>
<td><code>10010</code></td>
<td><code data-h>st</code></td>
<td><code data-h>0</code></td>
<td>Sets the value of address <code data-h>y</code> to <code data-h>x</code>, does not change <code data-h>dest</code></td>
</tr>
<tr>
<td><code>10011</code></td>
<td><code data-h>stn</code></td>
<td><code data-h>0</code></td>
<td>Sets the value of address <code>~<code data-h>y</code></code> to <code data-h>x</code>, does not change <code data-h>dest</code></td>
</tr>
<tr>
<td><code>11000</code></td>
<td><code data-h>jez</code></td>
<td><code data-h>y</code></td>
<td><code data-h>dest</code> is not changed if <code><code data-h>y</code> != <code data-h>0</code></code></td>
</tr>
<tr>
<td><code>11001</code></td>
<td><code data-h>jnz</code></td>
<td><code data-h>y</code></td>
<td><code data-h>dest</code> is not changed if <code><code data-h>y</code> == <code data-h>0</code></code></td>
</tr>
<tr>
<td><code>11010</code></td>
<td><code data-h>jlz</code></td>
<td><code data-h>y</code></td>
<td><code data-h>dest</code> is not changed if <code><code data-h>y</code> >= <code data-h>0</code></code></td>
</tr>
<tr>
<td><code>11011</code></td>
<td><code data-h>jgz</code></td>
<td><code data-h>y</code></td>
<td><code data-h>dest</code> is not changed if <code><code data-h>y</code> <= <code data-h>0</code></code></td>
</tr>
<tr>
<td><code>11100</code></td>
<td><code data-h>jcf</code></td>
<td><code data-h>y</code></td>
<td><code data-h>dest</code> is not changed if the carry flag is <code data-h>0</code></td>
</tr>
<tr>
<td><code>11101</code></td>
<td><code data-h>call</code></td>
<td><code data-h>y</code></td>
<td>Sets <code data-h>ra</code> to <code data-h>pc</code></td>
</tr>
<tr>
<td><code>11110</code></td>
<td><code data-h>swm16</code></td>
<td><code data-h>pc</code></td>
<td>Switches to 16-bit mode</td>
</tr>
<tr>
<td><code>11111</code></td>
<td><code data-h>stop</code></td>
<td><code data-h>pc</code></td>
<td>Stops the program</td>
</tr>
</tbody>
</table>
<h3>Instruction aliases</h3>
<table>
<thead>
<tr>
<th>Mnemonic</th>
<th>Actual</th>
</tr>
</thead>
<tbody>
<tr>
<td><code data-h>inc</code></td>
<td><code data-h>add y, 1</code></td>
</tr>
<tr>
<td><code data-h>dec</code></td>
<td><code data-h>sub y, 1</code></td>
</tr>
<tr>
<td><code data-h>alsh</code></td>
<td><code data-h>lsh</code></td>
</tr>
<tr>
<td><code data-h>jmp</code></td>
<td><code data-h>mov pc, y</code></td>
</tr>
<tr>
<td><code data-h>ret</code></td>
<td><code data-h>mov pc, ra</code></td>
</tr>
</tbody>
</table>
</div>
</main>
<script>
'use strict';
function printError(error, color = 'red') {
let text = (error instanceof Error ? ('stack' in error ? error.stack : error) : error).toString();
document.querySelectorAll('body > *').forEach(x => x.remove());
let elt = document.createElement('pre');
elt.style.color = color;
elt.style.paddingLeft = '15px';
elt.style.fontFamily = '\'Courier New\', Courier, monospace';
elt.style.overflowX = 'wrap';
elt.textContent = text.replaceAll('file:///media/fuse/drivefs-6804a76b943e4bfbed4d71622df9da15/root/code/', '');
(document.getElementById('.fake-node-devtools-real-body') ?? document.body).appendChild(elt);
let lastInterval = setInterval(() => {}, 0);
for (let j = 1; j <= lastInterval; j++) {
clearInterval(j);
}
window.onkeydown = () => {};
}
window.addEventListener('error', (event) => {
printError(event.error);
return true;
});
window.addEventListener('unhandledrejection', (event) => {
event.preventDefault();
printError(event.reason);
});
</script>
<script type="module">
import {EditorView, basicSetup} from 'https://esm.sh/codemirror@6.0.1';
import {EditorState, EditorSelection, SelectionRange} from 'https://esm.sh/@codemirror/state@^6.0.0';
import {StreamLanguage, HighlightStyle, syntaxHighlighting, indentUnit, StringStream} from 'https://esm.sh/@codemirror/language@6.10.8';
import {tags as t} from 'https://esm.sh/@lezer/highlight@1.2.1';
const LABEL_REGEX = /[a-zA-Z_][a-zA-Z0-9_]*/;
const NUMBER_REGEX = /-?(\d+|0b[01]+|0o[0-7]+|0x[0-9A-Fa-f]+)/;
const NORMAL_OPS = ['opcode', 'alsh', 'nop', 'and', 'xor', 'or', 'mov', 'not', 'clc', 'stc', 'add', 'sub', 'adc', 'sbb', 'lsh', 'rsh', 'arsh', 'ldn', 'stn', 'inc', 'dec', 'alsh', 'ld', 'st'];
const CONTROL_OPS = ['swm16', 'call', 'stop', 'jez', 'jnz', 'jlz', 'jgz', 'jcf', 'jmp', 'ret'];
function highlighter(stream) {
if (stream.eatSpace() || stream.match(',') || stream.match(':')) {
return null;
} else if (stream.match('#')) {
stream.skipToEnd();
return 'comment';
}
for (const op of CONTROL_OPS) {
if (stream.match(op)) {
return 'controlKeyword';
}
}
for (const op of NORMAL_OPS) {
if (stream.match(op)) {
return 'keyword';
}
}
if (stream.match(LABEL_REGEX)) {
return 'variableName';
} else if (stream.match(NUMBER_REGEX)) {
return 'number';
} else {
stream.next();
return 'invalid';
}
}
const startupCode = localStorage.getItem('orca-code') ?? `# fibonacci sequence program
mov a, 1
mov b, 1
loop:
add b, a, b
mov a, c
mov a, b
mov b, c
jmp loop`;
let editorSection = document.getElementById('editor');
const state = {
parent: editorSection,
extensions: [
basicSetup,
indentUnit.of(' '),
StreamLanguage.define({token: highlighter}),
EditorView.theme({}, {dark: true}),
syntaxHighlighting(HighlightStyle.define([
{tag: t.comment, color: '#6a9955'},
{tag: t.invalid, color: '#f44747'},
{tag: t.variableName, color: '#9cdcfe'},
{tag: t.number, color: '#b5cea8'},
{tag: t.keyword, color: '#569cd6'},
{tag: t.controlKeyword, color: '#c586c0'},
])),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
localStorage.setItem('orca-code', update.state.doc.toString());
}
if (update.selectionSet) {
let selection = update.state.selection;
selection.ranges.forEach(range => {
let lineEnd = update.state.doc.lineAt(range.head).to;
console.log(EditorSelection.create([new SelectionRange({anchor: lineEnd, head: lineEnd})]));
if (range.anchor > lineEnd) {
update.view.dispatch({selection: EditorSelection.create([new SelectionRange({anchor: lineEnd, head: lineEnd})])});
}
});
}
}),
],
};
let editor = new EditorView({...state, doc: startupCode});
document.getElementById('save-button').addEventListener('click', (event) => {
let filename = prompt('Enter filename:');
if (filename === null) {
return;
}
if (!filename.includes('.')) {
filename += '.orca';
}
let link = document.createElement('a');
link.href = URL.createObjectURL(new Blob([editor.state.doc.toString()], {type: 'text/asm'}));
link.download = filename;
document.body.appendChild(link);
link.click();
setTimeout(() => {
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
}, 0);
});
const pickerOptions = {
id: 'orca',
types: [
{
description: 'Assembly files',
accept: {
'text/asm': '.asm',
},
},
],
};
document.getElementById('load-button').addEventListener('click', async (event) => {
let handle;
try {
[handle] = await window.showOpenFilePicker(pickerOptions);
} catch (error) {
if (error instanceof DOMException) {
return;
} else {
throw error;
}
}
const file = await handle.getFile();
const code = await file.text();
editor.setState(EditorState.create({...state, doc: code}));
});
function highlight(text) {
let out = document.createElement('code');
for (const line of text.split('\n')) {
const stream = new StringStream(text, 4, ' ');
while (!stream.eol()) {
const oldPos = stream.pos;
const tag = highlighter(stream);
const text = stream.string.slice(oldPos, stream.pos);
if (tag !== null) {
let elt = document.createElement('span');
elt.className = 'h-' + tag;
elt.textContent = text;
out.appendChild(elt);
} else {
out.append(text);
}
}
}
return out;
}
window.addEventListener('load', () => {
document.querySelectorAll('code[data-h]').forEach((elt) => {
elt.replaceWith(highlight(elt.textContent));
});
});
class AssemblyError extends Error {
line;
constructor(msg, line) {
super(msg);
this.line = line;
}
}
const REGISTERS = ['0', 'pc', 'sp', 'ra', 'a', 'b', 'c', 'd'];
const OPCODES = ['nop', 'and', 'xor', 'or', 'mov', 'not', 'clc', 'stc', 'add', 'sub', 'adc', 'sbb', 'lsh', 'rsh', 'arsh', undefined, 'ld', 'ldn', 'st', 'stn', undefined, undefined, undefined, undefined, 'jez', 'jnz', 'jlz', 'jgz', 'jcf', 'call', 'swm16', 'stop'];
function assemble(code) {
code = code.split('\n').map(x => x.split('#')[0]);
let out = [];
out.log = [];
let labels = new Map();
for (let line = 1; line < code.length + 1; line++) {
const codeLine = code[line - 1].trim();
if (codeLine === '') {
continue;
}
let [op, ...args] = codeLine.split(' ');
args = args.join(' ').split(',').map(x => x.trim());
args = args.filter(arg => arg !== '');
if (op.endsWith(':')) {
if (args.length > 0) {
throw new AssemblyError('labels cannot have arguments', line);
}
const label = op.slice(0, -1);
if (REGISTERS.includes(label)) {
throw new AssemblyError(`invalid label: ${label}`, line);
}
labels.set(label, out.length);
continue;
}
let dest = '0';
let x = '0';
let y = '0';
let oneArgDest = false;
if (!OPCODES.includes(op)) {
if (op === 'inc') {
oneArgDest = true;
op = 'add';
y = '1';
} else if (op === 'dec') {
oneArgDest = true;
op = 'sub';
y = '1';
} else if (op === 'alsh') {
op = 'lsh';
} else if (op === 'jmp') {
op = 'mov';
dest = 'pc';
} else if (op === 'ret') {
op = 'mov';
dest = 'pc';
y = 'ra';
} else {
throw new AssemblyError(`invalid opcode: ${op}`, line);
}
}
op = OPCODES.indexOf(op);
if (args.length === 1) {
if (oneArgDest) {
dest = args[0];
} else {
y = args[0];
if (op & 0b11000 === 0b11000) {
dest = 'pc';
}
}
} else if (args.length === 2) {
if (op & 0b11000 === 0b11000) {
dest = 'pc';
x = args[0];
y = args[1];
} else {
dest = args[0];
y = args[1];
}
} else if (args.length === 3) {
dest = args[0];
x = args[1];
y = args[2];
} else {