You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
5.8 KiB
218 lines
5.8 KiB
<svelte:head>
|
|
<title>Buttons the Computer</title>
|
|
</svelte:head>
|
|
|
|
<script>
|
|
import {goto, stores} from '@sapper/app';
|
|
import { ButtonMachine } from 'buttons';
|
|
import Icon from '../components/Icon.svelte';
|
|
const { session } = stores();
|
|
|
|
let code = [
|
|
['PUSH', -10], // start at -10
|
|
['PUSH', 1], // increment by 1
|
|
['STOR', 'AX'],
|
|
['ADD'],
|
|
['JZ', 5], // if we're at 0 jumps to the end
|
|
['JUMP', 1] // the previous test fails so it jumps to loop again
|
|
];
|
|
|
|
let machine = new ButtonMachine(code);
|
|
|
|
let has_error = false;
|
|
|
|
$: has_error = machine.error != '';
|
|
|
|
const data_ops = new Set(['PUSH', 'JZ', 'JNZ', 'JUMP', 'STOR', 'RSTOR', 'HALT']);
|
|
|
|
const ops = ButtonMachine.operations();
|
|
const registers = ButtonMachine.register_names();
|
|
|
|
const change_op = (i, count) => {
|
|
let cur_op = code[i][0];
|
|
let cur_index = ops.indexOf(cur_op);
|
|
let new_index = (cur_index + count) < 0 ? ops.length - 1 : (cur_index + count) % ops.length;
|
|
|
|
code[i][0] = ops[new_index];
|
|
}
|
|
|
|
const op_has_data = (op) => data_ops.has(op);
|
|
|
|
const run_machine = () => {
|
|
machine.run();
|
|
machine = machine; // make svelte go
|
|
}
|
|
|
|
const step_machine = () => {
|
|
machine.step();
|
|
machine = machine;
|
|
}
|
|
|
|
const reset_machine = () => {
|
|
machine = new ButtonMachine(code);
|
|
}
|
|
|
|
const clone_line = (i) => {
|
|
let clone = code[i];
|
|
code.splice(i, 0, [...clone]);
|
|
code = code;
|
|
}
|
|
|
|
const delete_line = (i) => {
|
|
let item = code.splice(i, 1);
|
|
code = code;
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "sass/_variables";
|
|
|
|
#status-panel {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.active-line {
|
|
background-color: $green !important;
|
|
}
|
|
|
|
.empty-data {
|
|
width: 100%;
|
|
background-color: lighten($primary-color, 40%);
|
|
}
|
|
|
|
.off-canvas-toggle {
|
|
position: unset;
|
|
z-index: unset;
|
|
top: unset;
|
|
left: unset;
|
|
display: unset;
|
|
transition: unset;
|
|
padding: 5px;
|
|
}
|
|
|
|
#reg-select {
|
|
background-color: $gray;
|
|
width: 100%;
|
|
}
|
|
|
|
#reg-select select {
|
|
background-color: $gray;
|
|
width: 100%;
|
|
}
|
|
</style>
|
|
|
|
<div class="container grid-lg" id="content" data-testid="buttons-page">
|
|
<div class="columns">
|
|
|
|
<div class="column col-4 col-sm-12 col-md-12 col-xs-12">
|
|
<a alt="Open/Close help sidebar" aria-label="Open/Close help sidebar" class="off-canvas-toggle btn btn-primary btn-action" href="#sidebar-id">
|
|
?¿
|
|
</a>
|
|
|
|
<button class="btn btn-primary" on:click={ run_machine }>
|
|
<Icon code="►" tooltip="Run it" tooltip_right={ true } light={ true }/>
|
|
</button>
|
|
<button class="btn btn-primary" on:click={ step_machine }>
|
|
<Icon code="→" tooltip="Step through" tooltip_right={ true } light={ true }/>
|
|
</button>
|
|
<button class="btn btn-primary" on:click={ reset_machine }>
|
|
<Icon code="■" tooltip="Reset" light={ true }/>
|
|
</button>
|
|
|
|
<div class="divider"></div>
|
|
{#each code as [op, data], i}
|
|
<div class="input-group">
|
|
{#if has_error && machine.error_line == i}
|
|
<Icon name="alert-triangle" color="red" />
|
|
{:else}
|
|
{i}:
|
|
{/if}
|
|
|
|
<button class:active-line={ machine.ip == i }
|
|
on:click={ () => change_op(i, +1) }
|
|
on:contextmenu|preventDefault={ () => change_op(i, -1) }
|
|
class="btn btn-primary input-group-btn">{op}</button>
|
|
|
|
{#if op_has_data(op)}
|
|
{#if op === 'STOR' || op === 'RSTOR'}
|
|
<div class="form-group" id="reg-select">
|
|
<select class="form-select" bind:value={data}>
|
|
{#each registers as reg}
|
|
<option>{reg}</option>
|
|
{/each}
|
|
</select>
|
|
</div>
|
|
{:else if op == 'HALT'}
|
|
<input class:active-line={ machine.ip == i }
|
|
type="text" class="form-input" placeholder="register" bind:value={data}>
|
|
{:else}
|
|
<input class:active-line={ machine.ip == i }
|
|
type="number" class="form-input" placeholder="data" bind:value={data}>
|
|
{/if}
|
|
{:else}
|
|
<div class:active-line={ machine.ip == i } class="empty-data"></div>
|
|
{/if}
|
|
|
|
<button class="btn btn-primary input-group-addon"
|
|
on:click={ () => clone_line(i) }>
|
|
<Icon name="copy" tooltip='Clone it'/>
|
|
</button>
|
|
<button class="btn btn-primary input-group-addon"
|
|
on:click={ () => delete_line(i) }>
|
|
<Icon name="delete" tooltip='Delete it'/>
|
|
</button>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
|
|
<div class="column col-8 col-sm-12 col-md-12 col-xs-12" id="status-panel">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>IP</th>
|
|
<th>TICK</th>
|
|
<th>HALT</th>
|
|
<th>ERROR</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr class="active">
|
|
<td>{ machine.ip }</td>
|
|
<td>{ machine.tick }</td>
|
|
<td>{ machine.halted }</td>
|
|
<td>{ machine.error }</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<table class="table"> <thead>
|
|
<tr>
|
|
<th>STACK</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
{#each [...machine.stack].reverse() as datum, i}
|
|
<tr>
|
|
<td>{datum}</td>
|
|
</tr>
|
|
{/each}
|
|
</table>
|
|
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>REGISTER</th>
|
|
<th>VALUE</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{#each machine.register_entries as [name, value] }
|
|
<tr class="active">
|
|
<td>{ name }</td>
|
|
<td>{ value }</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|