UNPKG

orca-assembler

Version:

1,321 lines (1,210 loc) 59 kB
<!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> &nbsp;&nbsp;&nbsp; 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> &lt;&lt; <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> &lt;= <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 {