5 Commits

16 changed files with 3479 additions and 1220 deletions

View File

@ -9,9 +9,12 @@
"elm/browser": "1.0.2", "elm/browser": "1.0.2",
"elm/core": "1.0.5", "elm/core": "1.0.5",
"elm/html": "1.0.0", "elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3" "elm/json": "1.1.3"
}, },
"indirect": { "indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/time": "1.0.0", "elm/time": "1.0.0",
"elm/url": "1.0.0", "elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2" "elm/virtual-dom": "1.0.2"

View File

@ -1,3 +1,4 @@
@media (prefers-color-scheme: dark){
:root { :root {
--color-black: #05050F; --color-black: #05050F;
--color-white: #FFFFFF; --color-white: #FFFFFF;
@ -39,7 +40,7 @@
--color-body: var(--color-black); --color-body: var(--color-black);
--color-body-text: var(--color-black); --color-body-text: var(--color-white);
--color-addressbus: var(--color-addressbus-main); --color-addressbus: var(--color-addressbus-main);
--color-addressbus-text: var(--color-white); --color-addressbus-text: var(--color-white);
@ -73,18 +74,33 @@
--color-table-ram-head-text: var(--color-white); --color-table-ram-head-text: var(--color-white);
--color-table-ram-highlight: var(--color-ram-light2); --color-table-ram-highlight: var(--color-ram-light2);
--color-table-ram-highlight-text: var(--color-black); --color-table-ram-highlight-text: var(--color-black);
--color-table-ram-select: transparent;
--color-table-ram-select-text: inherit;
--color-table-ram-select-border: var(--color-grey-light2);
--color-table-cu-head: var(--color-cu-dark1); --color-table-cu-head: var(--color-cu-dark1);
--color-table-cu-head-text: var(--color-white); --color-table-cu-head-text: var(--color-white);
--color-table-cu-highlight: var(--color-cu-light2); --color-table-cu-highlight: var(--color-cu-light2);
--color-table-cu-highlight-text: var(--color-black); --color-table-cu-highlight-text: var(--color-black);
--color-table-cu-select: transparent;
--color-table-cu-select-text: inherit;
--color-table-cu-select-border: var(--color-grey-light2);
--color-arrow: var(--color-arrow-main); --color-arrow: var(--color-arrow-main);
--color-arrow-text: var(--color-white); --color-arrow-text: var(--color-white);
--color-arrow-text-hover: var(--color-white-light2); --color-arrow-text-hover: var(--color-white-light2);
--color-arrow-border: var(--color-black-light1); --color-arrow-border: var(--color-black-light1);
--color-modal: var(--color-black);
--color-modal-text: var(--color-white);
--color-modal-shadow: rgba( 0,0,0, 0.4 );
--color-code: var(--color-black-light1);
--color-code-text: var(--color-white);
} }
/* https://coolors.co/aa8f66-ed9b40-ffeedb-61c9a8-ba3b46-071013 */ /* https://coolors.co/aa8f66-ed9b40-ffeedb-61c9a8-ba3b46-071013 */
/* https://coolors.co/faf3dd-000022-9c528b-62a87c-247ba0-429ea6-8fb8de-72705b */ /* https://coolors.co/faf3dd-000022-9c528b-62a87c-247ba0-429ea6-8fb8de-72705b */
}

View File

@ -89,6 +89,13 @@
--color-arrow-text: var(--color-white); --color-arrow-text: var(--color-white);
--color-arrow-text-hover: var(--color-white-light2); --color-arrow-text-hover: var(--color-white-light2);
--color-arrow-border: var(--color-black-light1); --color-arrow-border: var(--color-black-light1);
--color-modal: var(--color-white-light1);
--color-modal-text: var(--color-black);
--color-modal-shadow: rgba( 0,0,0, 0.4 );
--color-code: var(--color-white-light2);
--color-code-text: var(--color-black);
} }

36
out/css/cookie.css Normal file
View File

@ -0,0 +1,36 @@
.cookie-banner{
position: fixed;
bottom: 0;
left: 0;
box-sizing: border-box;
width: 100vw;
height: min-content;
background-color: white;
border-top: 1px solid black;
padding: .75rem 2rem;
z-index: 2500;
}
.cookie-banner button {
display: inline;
height: 2.5rem;
padding: .5rem 1rem;
margin: 0;
line-height: 1em;
}
.cookie-banner * {
margin-right: 1rem !important;
vertical-align: baseline;
}
.cookie-banner > :first-child {
display: inline;
}
.cookie-banner > :last-child {
margin-right: 0 !important;
}

589
out/css/pc.css Normal file
View File

@ -0,0 +1,589 @@
/* COLORS */
.pc .addressbus {
background-color: var(--color-addressbus);
color: var(--color-addressbus-text);
}
.pc .databus {
background-color: var(--color-databus);
color: var(--color-databus-text);
}
.pc .ram {
background-color: var(--color-ram);
color: var(--color-ram-text);
}
.pc .cu {
background-color: var(--color-cu);
color: var(--color-cu-text);
}
.pc .alu {
background-color: var(--color-alu);
color: var(--color-alu-text);
}
.pc .section h1.header {
background-color: var(--color-header);
color: var(--color-header-text);
}
/* TABLE COLORS */
.pc .ram .scroller table thead.head th {
background-color: var(--color-table-ram-head);
color: var(--color-table-ram-head-text);
}
.pc .cu .scroller table thead.head th {
background-color: var(--color-table-cu-head);
color: var(--color-table-cu-head-text);
}
.pc .scroller table tr td {
background-color: var(--color-table-row);
color: var(--color-table-text);
border-bottom: 1px solid var(--color-table-border);
}
.pc .scroller table tr:nth-child(even) td{
background-color: var(--color-table-row-alt);
}
.pc .scroller table tr.empty td {
color: var(--color-table-text-empty);
}
.pc .ram .scroller table tr.current td {
background-color: var(--color-table-ram-highlight);
color: var(--color-table-ram-highlight-text);
}
.pc .cu .scroller table tr.current td {
background-color: var(--color-table-cu-highlight);
color: var(--color-table-cu-highlight-text);
}
.pc button,
.pc .button{
text-transform: none !important;
font-weight: 600;
height: 2.5em;
line-height: 1em;
margin: 0;
vertical-align: top;
padding: 0.25em 2em;
}
/* CONTROLS COLORS */
.controls button, .controls .button {
/* background-color: whitesmoke; */
color: var(--color-controls-button-text);
border-color: var(--color-controls-button-border);
}
.controls button:hover, .controls .button:hover{
color: var(--color-controls-button-text-hover);
}
.pc .controls .checker::before {
border: 1px solid var(--color-controls-button-border);
}
.pc .controls .checker.checked::before {
color: var(--color-controls-button-text);
}
/* FORMATTING STUFF */
.pc {
/*height: 400px;
height: 99vh;*/
box-sizing: border-box;
padding: 1em;
display: grid;
grid-template-columns: auto auto auto;
/* grid-template-columns: max-content auto max-content; */
gap: 9px;
}
.pc .grid-fullwidth{
grid-column: 1 / span 3;
}
.pc .section {
min-height: 25em;
max-height: calc(80vh - 8em);
min-width: 20%;
box-sizing: border-box;
padding: 2.5em 1em 1em 1em;
display: flex;
flex-direction: column;
position: relative;
/*top: 0;
left: 0;*/
}
.pc .section h1.header {
font-size: 1.2em;
position: absolute;
top: 3px;
left: 4px;
padding: 3px;
}
.pc .row {
display: flex;
justify-content: space-evenly;
}
.pc .scroller {
overflow-x: auto;
overflow-y: scroll;
/*position: relative;*/
/* max-height: 60%; */
padding: 0;
box-sizing: border-box;
width: 100%;
margin: 1em 0 0 0;
}
.pc .scroller:last-child {
margin-bottom: -1em;
}
.pc .scroller table {
margin: 0;
}
.pc .scroller table thead.head {
position: sticky;
top: -2px;
left: 0;
height: 1em;
padding: 1em;
transition: 0.2s;
}
.pc .scroller table thead th {
position: -webkit-sticky;
position: sticky;
top: 0;
}
.pc .scroller table thead.head.shrunk {
height: .5rem;
font-size: .8em;
}
.pc .scroller table thead.head th {
padding: 12px 4px 12px 15px;
transition: 0.2s;
}
.pc .scroller table thead.head.shrunk th {
padding-top: .2em;
padding-bottom: .2em;
}
.pc .scroller table .num {
text-align: right;
}
.pc .scroller table td:first-child,
.pc .scroller table td:last-child {
padding: 12px 15px;
}
.pc .scroller table tr.empty td {
padding: 3px 15px;
}
.pc .scroller table tbody button:last-child {
text-decoration: none;
text-transform: none;
height: max-content;
line-height: 2em;
font-weight: normal;
font-size: inherit;
padding: 0px 10px;
}
.pc .scroller tr:first-child td{
padding-top: 1.5rem !important;
}
th.address {
width: 5rem;
padding-left: 8px;
text-align: right;
}
.pc input[type=number] {
margin: 0 .5em 0 0;
padding: 0 .25em;
height: 2.5rem;
line-height: 1em;
}
.pc input[type=number].instruction {
width: 5em !important;
}
.pc input[type=number].address {
width: 7em !important;
}
/* CONTROLS */
.pc .controls > :not(:last-child) {
margin-right: 0.75em;
}
.pc .controls .checker {
display: inline-flex;
align-items: center;
padding-left: 3em;
}
.pc .controls .checker label {
display: inline;
/*font-size: 0.8em;*/
white-space: initial;
white-space: break-spaces;
text-transform: none;
width: 6em;
width: min-content;
vertical-align: middle;
line-height: .9em;
margin: 0;
cursor: pointer;
}
.pc .controls input[type=checkbox]{
display: none;
margin-left: -1em;
margin-right: 1em;
opacity: 0;
z-index: 0;
}
.pc .controls .checker::before{
content: "✔";
display: inline-block;
width: 2rem;
height: 2rem;
color: rgba(0,0,0,0);
border-radius: 4px;
vertical-align: middle;
margin: 0 .5em 0 -1em;
line-height: 1.8rem;
font-size: 1.4em;
box-sizing: border-box;
}
/* END CONTROLS */
/* RAM */
.pc .ram .scroller {
/* height: 100%; */
/* padding: 0.2em; */
box-sizing: border-box;
}
.pc .ram .scroller table {
width: 100%;
}
.pc .ram input[type=number] {
background-color: var(--color-table-ram-select);
color: var(--color-table-ram-select-text);
border-color: var(--color-table-ram-select-border);
}
.pc .ram table tr td:nth-child(2){
text-align: left;
}
.pc .ram .scroller table tbody button:last-child{
background-color: var(--color-table-ram-select);
color: var(--color-table-ram-select-text);
border-color: var(--color-table-ram-select-border);
}
/* CONTROL UNIT */
.pc .cu p {
margin: 0 0 .2em 0;
}
.pc .cu .scroller table {
width: 100%;
}
.pc .cu .input-row {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 0 .5em 0;
}
.pc .cu .input-row:last-child {
margin: 0;
}
.pc .cu input {
color: var(--color-cu-text);
border-color: var(--color-cu-text);
background-color: transparent;
}
.pc .cu input[type=number] {
width: 12.5em;
}
.pc .cu label {
display: inline;
margin-bottom: 0;
}
.pc .cu select{
margin: 0;
width: 100%;
background-color: var(--color-table-cu-select);
color: var(--color-table-cu-select-text);
border: 1px solid var(--color-table-cu-select-border);
}
.pc .cu .scroller table tbody button:last-child {
background-color: var(--color-table-cu-select);
color: var(--color-table-cu-select-text);
border: 1px solid var(--color-table-cu-select-border);
}
.pc .cu button,
.pc .cu .button {
height: 2.5rem;
box-sizing: border-box;
color: var(--color-cu-text);
border-color: var(--color-cu-text);
background-color: transparent;
}
/* ALU */
.pc .alu input, .pc .alu .button, .pc .alu button{
color: var(--color-alu-text);
border-color: var(--color-alu-text);
background-color: transparent;
}
.pc .alu button {
margin-right: 1em;
}
.pc .alu button:last-child {
margin-right: 0;
}
/* BUSSES */
.pc .databus,
.pc .addressbus{
/*position: relative;*/
text-align: left;
margin: 0;
padding: .75em .5em;
}
.pc .databus .label,
.pc .addressbus .label {
/* position: absolute; */
/* left: .5em; */
margin-right: 1em;
}
.pc .addressbus input {
color: var(--color-addressbus-text);
border-color: var(--color-addressbus-text);
background-color: var(--color-addressbus);
}
.pc .databus input {
color: var(--color-databus-text);
border-color: var(--color-databus-text);
background-color: var(--color-databus);
}
.pc .addressbus {
margin-bottom: 1em;
}
.pc .databus {
margin-top: 2em;
}
/* ARROWS */
.pc .arrow {
position: absolute;
bottom: 0;
left: 50%;
display: flex;
justify-content: center;
align-items: center;
width: 3rem;
height: 5rem;
/* transform: rotate(90deg) translate(1.5rem, 2.5rem) ; */
transform: translate(0, 3rem);
z-index: 100;
font-size: 0.5em;
color: var(--color-arrow-text);
background-color: var(--color-arrow);
}
.pc .arrow.down {
left: 25%;
}
.pc .arrow.up {
left: 75%;
transform: rotate(180deg) translate(0, -4.5rem);
}
.pc .arrow.up > :nth-child(1){
/* transform: rotate(90deg) translate(0rem, 2rem); */
transform: rotate(180deg);
}
.pc .arrow.up.arrow2 {
left: 50%;
}
.pc .arrow.top {
bottom: auto;
top: -6rem;
}
.pc .arrow > :nth-child(1) {
font-size: 1.2em;
line-height: 1.1em;
font-weight: 600;
color: var(--color-arrow-text) !important;
background-color: var(--color-arrow) !important;
border-color: var(--color-arrow-text) !important;
text-transform: none;
padding: 2px 10px;
margin: 0;
/* transform: rotate(-90deg) translate(0rem, -2rem); */
white-space: normal;
height: min-content;
}
.pc .arrow > :nth-child(1) *{
color: var(--color-arrow-text);
display: block;
white-space: break-spaces;
}
.pc .arrow > :nth-child(1):hover, .pc .arrow > :nth-child(1) *:hover {
color: var(--color-arrow-text-hover);
}
.pc .arrow > :nth-child(2) {
width: 0;
height: 0;
border-left: 2.5rem solid transparent;
border-right: 2.5rem solid transparent;
border-top: 2rem solid var(--color-arrow);
border-bottom: 0rem solid transparent;
position: absolute;
left: -1rem;
bottom: -2rem;
}
/* MODALS */
.modal {
position: fixed;
top: 0;
left: 0;
z-index: 1000;
box-sizing: border-box;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.modal.hidden {
display: none;
}
.modal-card {
z-index: 1500;
display: block;
width: 40em;
min-width: 45vw;
min-height: 5em;
position: relative;
box-sizing: border-box;
padding: 2.5em 2em 1em 2em;
background-color: var(--color-modal);
color: var(--color-modal-text);
}
.modal > :first-child {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--color-modal-shadow);
}
.modal .modal-close {
position: absolute;
top: .5em;
right: .5em;
}
.modal .modal-close::before {
content: "❌";
cursor: pointer;
}

View File

@ -1,511 +1,41 @@
/* COLORS */
body { body {
background-color: var(--color-body); background-color: var(--color-body);
color: var(--color-body-text); color: var(--color-body-text);
} }
.pc .addressbus { code {
background-color: var(--color-addressbus); background-color: var(--color-code);
color: var(--color-addressbus-text); border-color: var(--color-code-text);
color: var(--color-code-text);
} }
.pc .databus { .noscript {
background-color: var(--color-databus); box-sizing: border-box;
color: var(--color-databus-text); width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
} }
.pc .ram { .spacer {
background-color: var(--color-ram); height: 3rem;
color: var(--color-ram-text); width: 100%;
} }
.pc .cu { .hidden {
background-color: var(--color-cu); display: none !important;
color: var(--color-cu-text);
} }
.pc .alu { section.fullheight {
background-color: var(--color-alu); min-height: 100vh;
color: var(--color-alu-text);
} }
.pc .section h1.header { footer{
background-color: var(--color-header); margin-top: 20rem;
color: var(--color-header-text);
} }
/* TABLE COLORS */
.pc .ram .scroller table thead.head th {
background-color: var(--color-table-ram-head);
color: var(--color-table-ram-head-text);
}
.pc .cu .scroller table thead.head th {
background-color: var(--color-table-cu-head);
color: var(--color-table-cu-head-text);
}
.pc .scroller table tr td {
background-color: var(--color-table-row);
color: var(--color-table-text);
border-bottom: 1px solid var(--color-table-border);
}
.pc .scroller table tr:nth-child(even) td{
background-color: var(--color-table-row-alt);
}
.pc .scroller table tr.empty td {
color: var(--color-table-text-empty);
}
.pc .ram .scroller table tr.current td {
background-color: var(--color-table-ram-highlight);
color: var(--color-table-ram-highlight-text);
}
.pc .cu .scroller table tr.current td {
background-color: var(--color-table-cu-highlight);
color: var(--color-table-cu-highlight-text);
}
/* CONTROLS COLORS */
.controls button, .controls .button {
/* background-color: whitesmoke; */
color: var(--color-controls-button-text);
border-color: var(--color-controls-button-border);
}
.controls button:hover, .controls .button:hover{
color: var(--color-controls-button-text-hover);
}
.pc .controls .checker::before {
border: 1px solid var(--color-controls-button-border);
}
.pc .controls .checker.checked::before {
color: var(--color-controls-button-text);
}
/* FORMATTING STUFF */
button {
margin: 0;
}
.pc {
height: 400px;
height: 99vh;
box-sizing: border-box;
padding: 1em;
display: grid;
grid-template-columns: auto auto auto;
/* grid-template-columns: max-content auto max-content; */
gap: 9px;
}
.pc .grid-fullwidth{
grid-column: 1 / span 3;
}
.pc .section {
min-height: 25em;
max-height: calc(80vh - 8em);
min-width: 20%;
box-sizing: border-box;
padding: 2.5em 1em 1em 1em;
display: flex;
flex-direction: column;
position: relative;
/*top: 0;
left: 0;*/
}
.pc .section h1.header {
font-size: 1.2em;
position: absolute;
top: 3px;
left: 4px;
padding: 3px;
}
.pc .row {
display: flex;
justify-content: space-evenly;
}
.pc .scroller {
overflow-x: auto;
overflow-y: scroll;
/*position: relative;*/
/* max-height: 60%; */
padding: 0;
box-sizing: border-box;
width: 100%;
margin: 1em 0 0 0;
}
.pc .scroller:last-child {
margin-bottom: -1em;
}
.pc .scroller table thead.head {
position: sticky;
top: -2px;
left: 0;
height: 1em;
padding: 1em;
transition: 0.2s;
}
.pc .scroller table thead th {
position: -webkit-sticky;
position: sticky;
top: 0;
}
.pc .scroller table thead.head.shrunk {
height: .5em;
font-size: .8em;
}
.pc .scroller table thead.head th {
padding: 12px 4px 12px 15px;
transition: 0.2s;
}
.pc .scroller table thead.head.shrunk th {
padding-top: .2em;
padding-bottom: .2em;
}
.pc .scroller table .num {
text-align: right;
}
.pc .scroller table td:first-child,
.pc .scroller table td:last-child {
padding: 12px 15px;
}
.pc .scroller table tr.empty td {
padding: 3px 15px;
}
.pc .scroller table tbody button:last-child {
text-decoration: none;
text-transform: none;
height: max-content;
line-height: 2em;
font-weight: normal;
font-size: inherit;
padding: 0px 10px;
}
th.address {
width: 5rem;
padding-left: 8px;
text-align: right;
}
.pc input[type=number] {
margin: 0 .5em 0 0;
padding: 0 .25em;
height: 1.5em;
line-height: 1em;
}
/* CONTROLS */
.pc .controls button,
.pc .controls .button{
text-transform: none !important;
font-weight: 600;
height: 2.5em;
line-height: 1em;
margin: 0;
vertical-align: top;
padding: 0.25em 2em;
}
.pc .controls > :not(:last-child) {
margin-right: 0.75em;
}
.pc .controls .checker {
display: inline-flex;
align-items: center;
padding-left: 3em;
}
.pc .controls .checker label {
display: inline;
/*font-size: 0.8em;*/
white-space: initial;
white-space: break-spaces;
text-transform: none;
width: 6em;
width: min-content;
vertical-align: middle;
line-height: .9em;
margin: 0;
cursor: pointer;
}
.pc .controls input[type=checkbox]{
display: none;
margin-left: -1em;
margin-right: 1em;
opacity: 0;
z-index: 0;
}
.pc .controls .checker::before{
content: "✔";
display: inline-block;
width: 2rem;
height: 2rem;
color: rgba(0,0,0,0);
border-radius: 4px;
vertical-align: middle;
margin: 0 .5em 0 -1em;
line-height: 1.8rem;
font-size: 1.4em;
box-sizing: border-box;
}
/* END CONTROLS */
/* RAM */
.pc .ram .scroller {
/* height: 100%; */
/* padding: 0.2em; */
box-sizing: border-box;
}
.pc .ram .scroller table {
width: 100%;
}
.pc .ram input[type=number] {
background-color: var(--color-table-ram-select);
color: var(--color-table-ram-select-text);
border-color: var(--color-table-ram-select-border);
}
.pc .ram input[type=number].instruction {
width: 5em;
}
.pc .ram input[type=number].address {
width: 7em;
}
.pc .ram table tr td:nth-child(2){
text-align: left;
}
.pc .ram .scroller table tbody button:last-child{
background-color: var(--color-table-ram-select);
color: var(--color-table-ram-select-text);
border-color: var(--color-table-ram-select-border);
}
/* CONTROL UNIT */
.pc .cu p {
margin: 0 0 .2em 0;
}
.pc .cu .scroller table {
width: 100%;
}
.pc .cu .input-row {
display: flex;
justify-content: space-between;
align-items: baseline;
margin: 0 0 .5em 0;
}
.pc .cu .input-row:last-child {
margin: 0;
}
.pc .cu input {
color: var(--color-cu-text);
border-color: var(--color-cu-text);
background-color: transparent;
}
.pc .cu label {
display: inline;
}
.pc .cu select{
margin: 0;
width: 100%;
background-color: var(--color-table-cu-select);
color: var(--color-table-cu-select-text);
border: 1px solid var(--color-table-cu-select-border);
}
.pc .cu .scroller table tbody button:last-child {
background-color: var(--color-table-cu-select);
color: var(--color-table-cu-select-text);
border: 1px solid var(--color-table-cu-select-border);
}
/* ALU */
.pc .alu input {
color: var(--color-alu-text);
border-color: var(--color-alu-text);
background-color: transparent;
}
/* BUSSES */
.pc .databus,
.pc .addressbus{
/*position: relative;*/
text-align: left;
margin: 0;
padding: .75em .5em;
}
.pc .databus .label,
.pc .addressbus .label {
/* position: absolute; */
/* left: .5em; */
margin-right: 1em;
}
.pc .addressbus input {
color: var(--color-addressbus-text);
border-color: var(--color-addressbus-text);
background-color: var(--color-addressbus);
}
.pc .databus input {
color: var(--color-databus-text);
border-color: var(--color-databus-text);
background-color: var(--color-databus);
}
.pc .addressbus {
margin-bottom: 1em;
}
.pc .databus {
margin-top: 2em;
}
/* ARROWS */
.arrow {
position: absolute;
bottom: 0;
left: 50%;
display: flex;
justify-content: center;
align-items: center;
width: 3rem;
height: 5rem;
/* transform: rotate(90deg) translate(1.5rem, 2.5rem) ; */
transform: translate(0, 3rem);
z-index: 100;
font-size: 0.5em;
color: var(--color-arrow-text);
background-color: var(--color-arrow);
}
.arrow.down {
left: 25%;
}
.arrow.up {
left: 75%;
transform: rotate(180deg) translate(0, -4.5rem);
}
.arrow.up > :nth-child(1){
/* transform: rotate(90deg) translate(0rem, 2rem); */
transform: rotate(180deg);
}
.arrow.up.arrow2 {
left: 50%;
}
.arrow.top {
bottom: auto;
top: -6rem;
}
.arrow > :nth-child(1) {
font-size: 1.2em;
line-height: 1.1em;
font-weight: 600;
color: var(--color-arrow-text);
background-color: var(--color-arrow);
border-color: var(--color-arrow-text);
text-transform: none;
padding: 2px 10px;
margin: 0;
/* transform: rotate(-90deg) translate(0rem, -2rem); */
white-space: normal;
height: min-content;
}
.arrow > :nth-child(1) *{
color: var(--color-arrow-text);
display: block;
white-space: break-spaces;
}
.arrow > :nth-child(1):hover, .arrow > :nth-child(1) *:hover {
color: var(--color-arrow-text-hover);
}
.arrow > :nth-child(2) {
width: 0;
height: 0;
border-left: 2.5rem solid transparent;
border-right: 2.5rem solid transparent;
border-top: 2rem solid var(--color-arrow);
border-bottom: 0rem solid transparent;
position: absolute;
left: -1rem;
bottom: -2rem;
}

17
out/examples-list.json Normal file
View File

@ -0,0 +1,17 @@
{
"available":
[
{
"title": "Simple Counter",
"version": "0.1",
"url": "examples/simple-counter.json",
"enabled": 1
},
{
"title": "Add 2 numbers(placeholder)",
"version": "0.1",
"url": "examples/adding.json",
"enabled": 1
}
]
}

View File

@ -0,0 +1 @@
{"model-version":1,"pc":{"addressBus":0,"dataBus":0,"instructionReg":0,"programmCounter":0,"uCounter":0,"accumulator":0,"ram":[100005,300000,200005,400000,0,5,0,0,0,0,0]},"uCode":["pc2ab","ram2db","db2ir","pcInc","ir2uc","n","n","n","n","n","ir2ab","ram2db","db2acc","ucReset","n","n","n","n","n","n","acc2db","ir2ab","db2ram","ucReset","n","n","n","n","n","n","accInc","ucReset","n","n","n","n","n","n","n","n","ir2pc","ucReset","n","n","n","n","n","n","n","n"],"autoscroll":true}

10
out/icon/cross.svg Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Cross" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
<path d="M14.348,14.849c-0.469,0.469-1.229,0.469-1.697,0L10,11.819l-2.651,3.029c-0.469,0.469-1.229,0.469-1.697,0
c-0.469-0.469-0.469-1.229,0-1.697l2.758-3.15L5.651,6.849c-0.469-0.469-0.469-1.228,0-1.697c0.469-0.469,1.228-0.469,1.697,0
L10,8.183l2.651-3.031c0.469-0.469,1.228-0.469,1.697,0c0.469,0.469,0.469,1.229,0,1.697l-2.758,3.152l2.758,3.15
C14.817,13.62,14.817,14.38,14.348,14.849z"/>
</svg>

After

Width:  |  Height:  |  Size: 835 B

View File

@ -4,9 +4,9 @@
<!-- Basic Page Needs <!-- Basic Page Needs
--> -->
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Zähler</title> <title>Johnny Simulator</title>
<meta name="description" content="" /> <meta name="description" content="Web based Johhny-Simulator" />
<meta name="author" content="" /> <meta name="author" content="Christian" />
<!-- Mobile Specific Metas <!-- Mobile Specific Metas
--> -->
@ -16,7 +16,10 @@
--> -->
<link rel="stylesheet" href="css/normalize.css" /> <link rel="stylesheet" href="css/normalize.css" />
<link rel="stylesheet" href="css/skeleton.css" /> <link rel="stylesheet" href="css/skeleton.css" />
<link rel="stylesheet" href="css/cookie.css" />
<link rel="stylesheet" href="css/colors-light.css"> <link rel="stylesheet" href="css/colors-light.css">
<link rel="stylesheet" href="css/colors-dark.css">
<link rel="stylesheet" href="css/pc.css" />
<link rel="stylesheet" href="css/style.css" /> <link rel="stylesheet" href="css/style.css" />
<!-- Favicon <!-- Favicon
@ -27,11 +30,360 @@
<body> <body>
<!-- Primary Page Layout <!-- Primary Page Layout
--> -->
<div id="elm"></div> <div id="elm">
<div class="noscript">Sorry, but this app needs Javascript to run :/</div>
</div>
<div class="spacer"></div>
<section class="container fullheight">
<div id="langPicker">
<button id="langPickerDE">German</button>
<button id="langPickerEN">English</button>
</div>
<div id="langDE" class="hidden">
<h1>Wie diese App funktioniert</h1>
<p>
Hallo, schön dass du dir diese Seite anschaust.<br>
Damit du besser verstehst, wie diese Anwendung funktioniert, hier einmal die Basics:
</p>
<h2>Kontrollknöpfe</h2>
<p>
Ganz oben findest du eine Reihe mit Kontrollknöpfen. Jeder Knopf hat seine eigene Funktion.
</p>
<ul>
<li>
<b>µCycle</b>
Mit diesem Knopf kannst du einen einzelnen Schritt im µCode ausführen.
</li>
<li>
<b>Instruction</b>
Hiermit kannst du eine ganze Instruction ausführen lassen und musst nicht durch jeden µCode Schritt einzeln durchdrücken.
</li>
<li>
<b>Reset PC</b>
Damit kannst du den PC zurücksetzten. Dabei bleibt der RAM und der µCode erhalten.
</li>
<li>
<b>Autoscroll</b>
Nachdem im RAM oder im µCode ein anderer Eintrag ausgewählt wurde, kann automatisch zum Eintrag gescrollt werden
</li>
<li>
<b>Load Example</b>
Mit diesem Knopf kannst du ein Beispielprogramm in den PC laden. Dabei wird dein momentanes Programm überschrieben.
</li>
</ul>
<h2>Bausteine</h2>
<p>
Der Computer ist in mehrere Bausteine aufgeteilt. Jeder Baustein hat eine eigene spezielle Funktion. Ein echter Computer ist natürlich deutlich komplizierter, aber so ist der PC deutlich verständlicher.
</p>
<h3>RAM</h3>
<p>
Im Arbeitsspeicher wird das Programm gespeichert. Jeder Eintrag ist dabei in ein Low-Byte und ein High-Byte unterteilt. Dadurch kann man in einem Eintrag die Instruktion und Addresse einfacher unterscheiden.<br>
<a href="#befehl">Siehe Befehle</a>
</p>
<h3>Control Unit</h3>
<p>
Wie der Name es schon sagt, steuert und verwaltet die Control Unit alle Vorgänge im Computer. Das passiert dadurch, dass eine Instruktion in viele kleine µCodes aufgeteilt wird.
</p><p>
Diese µCodes sind in der Tabelle aufgeführt. Jede Instruktion besteht hier aus bis zu 10 µCodes. <br>
Bei einem Befehl z.B. <code>001 00000</code> ist die Instruktion in den ersten drei Stellen zu finden. Dieser Wert wird dann mit 10 multipliziert und in den µCode Counter geladen. <br>
In diesem Fall stände dann <code>0010</code> im Counter.<br>
In der Tabelle sind dann alle Befehle für die Instruktion <code>001</code> zu finden.
</p>
<h3>ALU</h3>
<p>
Die Arithmetik Logic Unit ist hier sehr einfach aufgebaut.
</p>
<p>
Sie kann den Akkumulator nur erhöhen oder erniedrigen. Dafür können Werte aber direkt vom Datenbus geladen werden.
</p>
<h3>Datenbus</h3>
<p>
Über den Datenbus können Daten und Befehle zwischen RAM, Control Unit und ALU übertragen werden.
</p>
<h3>Addressbus</h3>
<p>
Über diesen Bus kann die Control Unit steuern, welcher Wert im RAM ausgewählt wird.<br>
Bei normalen Rechnern sind hier natürlich noch deutlich mehr Werte angeschlossen.
</p>
<a id="befehl"></a>
<h2>Befehle</h2>
<p>
Ein Befehl besteht aus einer Instruktion und einem Argument. Deshalb sind im RAM und im Instruction Register alle Werte separiert.
</p>
<pre>
<code>
0 0 0 0 0 0 0 0
Instr Argument
</code>
</pre>
<h2>µCodes</h2>
<p>
Eine Instruktion kann nicht direkt ausgeführt werden. Deshalb wird sie durch mehrere kleine Befehle, sog. µCodes zusammengesetzt.
</p>
<table>
<thead>
<tr>
<th>µCode</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td>ProgCounter -> AddrBus</td>
<td>Lade den Wert des Programm Counters in den Adressbus</td>
</tr>
<tr>
<td>InstrReg -> ProgCounter</td>
<td>Lade das Argument des Instruktionsregisters in den Programm Counter</td>
</tr>
<tr>
<td>ProgCounter ++</td>
<td>Erhöhe den Wert des Programm Counter um 1</td>
</tr>
<tr>
<td>Acc == 0 => InstReg -> ProgCounter</td>
<td>Wenn der Wert des Akkumulators 0 ist, dann lade das Arggument des Instruktionsregisters in den Programm Counter</td>
</tr>
<tr>
<td>Ram -> DataBus</td>
<td>Schreibe den aktuellen Wert aus den RAM auf den Datembus</td>
</tr>
<tr>
<td>DataBus -> Ram</td>
<td>Schreibe den Wert des Datenbus in die momentane Stelle des RAM</td>
</tr>
<tr>
<td>DataBus -> InstReg</td>
<td>Lade den Befehl vom Datenbus in das Instruktionsregister</td>
</tr>
<tr>
<td>DataBus -> Acc</td>
<td>Lade den Wert vom Datenbus in den Akkumulator der ALU</td>
</tr>
<tr>
<td>Acc -> DataBus</td>
<td>Schreibe den Wert vom Akkumulator auf den Datenbus</td>
</tr>
<tr>
<td>Acc ++</td>
<td>Erhöhe den Wert des Akkumulators um 1</td>
</tr>
<tr>
<td>Acc --</td>
<td>Verringere den Wert des Akkumulators um 1</td>
</tr>
<tr>
<td>InstReg -> µCounter</td>
<td>Nehme das Argument des Befehls im Instruktionsregister, füge am Ende eine 0 an und Lade ihn in den µCounter</td>
</tr>
<tr>
<td>InstReg -> AddrBus</td>
<td>Lade das Argument im Instruktionsregister in den Addressbus</td>
</tr>
<tr>
<td>µCounter = 0</td>
<td>Setzte den µCounter zurück</td>
</tr>
</tbody>
</table>
<h1>Source</h1>
<p>
Im Moment ist dieser Code noch nicht ordentlich genug, dass es Sinn hat ihn zu veröffentlichen.
</p>
<p>
Dieses Projekt ist angelehnt an den <a href="https://sourceforge.net/projects/johnnysimulator/" target="_blank">Johnny-Simulator</a>.
</p>
</div>
<div id="langEN">
<h1>How this App works</h1>
<p>
Hello. In order for you to understand what this App does, I want to show you some basics of this app.
</p>
<h2>Control Buttons</h2>
<p>
At the top of the application you can see a row of control buttons:
</p>
<ul>
<li>
<b>µCycle</b>
With this button you can execute a single µCode Step.
</li>
<li>
<b>Instruction</b>
This button executes a whole instruction at a time. This way you don't have to press µCycle all the time.
</li>
<li>
<b>Reset PC</b>
With this, you can reset the pc. Please note that ram und µCodes are not reset.
</li>
<li>
<b>Autoscroll</b>
After a value in RAM or in the µCodes is selected you can automatically scroll to them
</li>
<li>
<b>Load Example</b>
Click this button and select an example you want to load. This will overwrite the whole pc including ram and µCode
</li>
</ul>
<h2>Blocks</h2>
<p>
This computer is made up by multiple blocks like any other neumann-maschiene. Every block has it's own unique set of functions. A normal computer is much more complex than this, but the principle is the same.
</p>
<h3>RAM</h3>
<p>
The current programm is stored in the RAM. Every entry is seperated as mentioned in <a href="#entry">entry</a>.
</p>
<h3>Control Unit</h3>
<p>
As the name suggests, the control unit manages everything that is done. To know what needs to be done it has a list of µCodes that gets executed when a instruction is requested.
</p><p>
These µCodes can be found in the table. Every instruction consists of up to 10 µCodes. <br>
When a entry is executed, the first three digits (the instruction) gets multiplied by 10. This now is written into the µCounter and the µCodes for it get's executed.
</p><p>
Example:<br>
When the entry <code>001 00000</code> gets executed, the µCounter would be set to <code>0010</code>. Now every µCode starting at location <code>0010</code> in the µCodes gets executed.
</p>
<h3>ALU</h3>
<p>
The Arithmetic Logic Unit is pretty simple in this case.
</p>
<p>
The ALU can only increment or decrement the accumulator. But it can read numbers from databus.
</p>
<h3>Databus</h3>
<p>
Via the Databus values and entrys can be transported between RAM, Control Unit and ALU.
</p>
<h3>Addressbus</h3>
<p>
With the addressbus the Control Unit can control which value is selected in RAM. On a normal computer there are a lot mor things attached to this bus.
</p>
<a id="entry"></a>
<h2>Entry</h2>
<p>
Each entry consits of a instruction and an argument. That's why the entrys in RAM and in µCodes are displayed seperatly.
</p>
<pre>
<code>
0 0 0 0 0 0 0 0
Instr Argument
</code>
</pre>
<h2>µCodes</h2>
<p>
Every instruction consists of a list of µCodes. Here is an explanaition for all of them:
</p>
<table>
<thead>
<tr>
<th>µCode</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>ProgCounter -> AddrBus</td>
<td>Load the value from programm counter to addressbus</td>
</tr>
<tr>
<td>InstrReg -> ProgCounter</td>
<td>Load the argument from instruction register to programm counter</td>
</tr>
<tr>
<td>ProgCounter ++</td>
<td>Increment programm counter by 1</td>
</tr>
<tr>
<td>Acc == 0 => InstReg -> ProgCounter</td>
<td>If the accumulator is 0 then write the argument from instruction register to programm counter</td>
</tr>
<tr>
<td>Ram -> DataBus</td>
<td>Load selected value from RAM to databus</td>
</tr>
<tr>
<td>DataBus -> Ram</td>
<td>Write value from databus to selected value in RAM</td>
</tr>
<tr>
<td>DataBus -> InstReg</td>
<td>Write entry from databus to instruction register</td>
</tr>
<tr>
<td>DataBus -> Acc</td>
<td>Write value from databus to accumulator</td>
</tr>
<tr>
<td>Acc -> DataBus</td>
<td>Load value from accumulator to databus</td>
</tr>
<tr>
<td>Acc ++</td>
<td>Increment accumulator by 1</td>
</tr>
<tr>
<td>Acc --</td>
<td>Decrement accumulator by 1</td>
</tr>
<tr>
<td>InstReg -> µCounter</td>
<td>Take the instruction from instruction register, add a 0 at the tail and write it to µCounter</td>
</tr>
<tr>
<td>InstReg -> AddrBus</td>
<td>Write the argument from instruction register to addressbus</td>
</tr>
<tr>
<td>µCounter = 0</td>
<td>Set µCounter to 0</td>
</tr>
</tbody>
</table>
<h1>Source</h1>
<p>
At the moment the code for this project isn't good enough to share it online.
</p>
<p>
This project is my take on <a href="https://sourceforge.net/projects/johnnysimulator/" target="_blank">Johnny-Simulator</a>.
</p>
</div>
</section>
<footer>
<div class="container">
<p>
Made with ☕
by <a href="https://github.com/ChrisgammaDE" target="_blank">Christian</a>
</p>
<p>
<a href="https://yokta.de/i/impressum.html" target="_blank">Impressum</a>
</p>
</div>
</footer>
<!-- End Document <!-- End Document
--> -->
<script src="elm.js"></script> <script src="js/elm.js"></script>
<script src="script.js"></script> <script src="js/cookie.js"></script>
<script src="js/pc.js"></script>
<script src="js/script.js"></script>
</body> </body>
</html> </html>

65
out/js/cookie.js Normal file
View File

@ -0,0 +1,65 @@
// Check if cookie banner has been accepted by running cookieAccepted()
// Please import cookie.css!!!!
function __cookie_internal_addBanner__(){
let banner = document.createElement("div");
let text = document.createElement("div");
let link_datenschutz = document.createElement("a");
let button_accept = document.createElement("button");
let button_deny = document.createElement("button");
banner.classList.add("cookie-banner");
text.innerText =
"In order for this computer to keep it's data after reloading,\
this page uses cookies and localStorage. ";
link_datenschutz.href = "https://yokta.de/i/datenschutz.html";
link_datenschutz.innerText = "Privacy Policy";
link_datenschutz.target = "_blank";
text.appendChild( link_datenschutz );
button_accept.innerText = "I'm ok with that"
button_deny.innerText = "Not on my watch!"
button_accept.addEventListener("click", (message) => {
localStorage.setItem("cookie", "accepted");
let parent = message.target.parentElement;
document.body.removeChild(parent);
});
button_deny.addEventListener("click", (message) => {
let parent = message.target.parentElement;
document.body.removeChild(parent);
});
banner.appendChild( text );
banner.appendChild( button_accept );
banner.appendChild( button_deny );
document.body.appendChild(banner);
}
// Returns true if cookies are accepted
function cookieAccepted() {
let maybe_cookie_status = localStorage.getItem("cookie");
let cookie_status = 0; // 0 = not answered, 1 = accepted, 2 = denied
if( maybe_cookie_status == null ) cookie_status = 0;
else if(maybe_cookie_status == "accepted") cookie_status = 1;
else cookie_status = 2;
if (cookie_status == 1) return true;
if (cookie_status == 0) {
let maybe_banner = document.getElementsByClassName("cookie-banner")[0];
if(maybe_banner == undefined){
__cookie_internal_addBanner__();
}
return false;
}
return false;
}
cookieAccepted();

File diff suppressed because it is too large Load Diff

89
out/js/pc.js Normal file
View File

@ -0,0 +1,89 @@
// Init Elm
var app = Elm.Main.init({
node: document.getElementById("elm"),
// flags: Date.now()
});
// Dom Objects
let pc = document.getElementsByClassName("pc")[0];
let pc_ram = pc.getElementsByClassName("ram")[0];
let pc_cu = pc.getElementsByClassName("cu")[0];
let pc_alu = pc.getElementsByClassName("alu")[0];
let pc_ram_scroller = pc_ram.getElementsByClassName("scroller")[0];
let pc_cu_scroller = pc_cu.getElementsByClassName("scroller")[0];
let control_autoscroll = document.getElementById("enableScrolling");
// Methods
function scrollToCurrent(){
if( control_autoscroll.checked == false ) return;
let current_ram = pc_ram.getElementsByClassName("current")[0];
let current_uCode = pc_cu.getElementsByClassName("current")[0];
if( pc.scrollIntoViewIfNeeded == undefined ){
if( typeof current_ram != "undefined"){
current_ram.scrollIntoView({behavior: "smooth", block: "nearest"});
}
if( typeof current_uCode != "undefined"){
current_uCode.scrollIntoView({behavior: "smooth", block: "nearest"});
}
}else{
if( typeof current_ram != "undefined"){
current_ram.scrollIntoViewIfNeeded({behavior: "smooth", block: "nearest"});
}
if( typeof current_uCode != "undefined"){
current_uCode.scrollIntoViewIfNeeded({behavior: "smooth", block: "nearest"});
}
}
// pc.scrollIntoView();
}
function shrinkTableHead(scroll){
let scroller = scroll.target;
let pos = scroller.scrollTop;
let thead = scroller.getElementsByClassName("head")[0];
if( pos > 70 ){
thead.classList.add("shrunk");
}else if (pos < 40){
thead.classList.remove("shrunk");
}
thead.classList.add("shrunk");
}
function loadStorage() {
let content = localStorage.getItem("pc_data");
if( typeof content == "string" ){
app.ports.localStorageRecieve.send( content );
}
}
// Load last state of pc
loadStorage();
// EVENT LISTENERS
pc_ram_scroller.addEventListener("scroll", shrinkTableHead);
pc_cu_scroller.addEventListener("scroll", shrinkTableHead);
// Recieve Elm updates via ports
app.ports.sendUUpdate.subscribe( (message) => {
// console.log("Update: ", message);
scrollToCurrent();
// Make sure that even when the calculation takes longer it will scroll correctly
setTimeout( scrollToCurrent, 100 );
} );
// Recieve LocalSession Updates via Ports
app.ports.localStorageSend.subscribe( (message) => {
// console.log("localSessionSend: ", message);
if( cookieAccepted() )
localStorage.setItem("pc_data", message);
} );

26
out/js/script.js Normal file
View File

@ -0,0 +1,26 @@
let text_de = document.getElementById("langDE");
let text_en = document.getElementById("langEN");
let btn_changeToDe = document.getElementById("langPickerDE");
let btn_changeToEn = document.getElementById("langPickerEN");
let lang = (typeof document.documentElement.lang == "string") ? document.documentElement.lang: "en"
function changeLang(lang){
if(lang == "de"){
text_de.classList.remove("hidden");
text_en.classList.add("hidden");
}else{
text_en.classList.remove("hidden");
text_de.classList.add("hidden");
}
}
btn_changeToEn.addEventListener("click", () => { changeLang("en") } );
btn_changeToDe.addEventListener("click", () => { changeLang("de") } );
// Do stuff on loading
changeLang( lang );

View File

@ -1,76 +1,26 @@
// Init Elm let text_de = document.getElementById("langDE");
var app = Elm.Main.init({ let text_en = document.getElementById("langEN");
node: document.getElementById("elm"),
// flags: Date.now()
});
// Dom Objects let btn_changeToDe = document.getElementById("langPickerDE");
let pc = document.getElementsByClassName("pc")[0]; let btn_changeToEn = document.getElementById("langPickerEN");
let pc_ram = pc.getElementsByClassName("ram")[0];
let pc_cu = pc.getElementsByClassName("cu")[0];
let pc_alu = pc.getElementsByClassName("alu")[0];
let pc_ram_scroller = pc_ram.getElementsByClassName("scroller")[0]; let lang = (typeof document.documentElement.lang == "string") ? document.documentElement.lang: "en"
let pc_cu_scroller = pc_cu.getElementsByClassName("scroller")[0];
let control_autoscroll = document.getElementById("enableScrolling");
// Methods function changeLang(lang){
function scrollToCurrent(){ if(lang == "de"){
if( control_autoscroll.checked == false ) return; text_de.classList.remove("hidden");
text_en.classList.add("hidden");
let current_ram = pc_ram.getElementsByClassName("current")[0]; }else{
if( typeof current_ram != "undefined"){ text_en.classList.remove("hidden");
current_ram.scrollIntoView({behavior: "smooth", block: "center"}); text_de.classList.add("hidden");
} }
let current_uCode = pc_cu.getElementsByClassName("current")[0];
if( typeof current_uCode != "undefined"){
current_uCode.scrollIntoView({behavior: "smooth", block: "center"});
}
} }
btn_changeToEn.addEventListener("click", () => { changeLang("en") } );
function shrinkTableHead(scroll){ btn_changeToDe.addEventListener("click", () => { changeLang("de") } );
let scroller = scroll.target;
let pos = scroller.scrollTop;
let thead = scroller.getElementsByClassName("head")[0];
if( pos > 70 ){
thead.classList.add("shrunk");
}else if (pos < 40){
thead.classList.remove("shrunk");
}
}
function loadStorage() {
let content = localStorage.getItem("pc_data");
if( typeof content == "string" ){
app.ports.localStorageRecieve.send( content );
}
}
// Load last state of pc // Do stuff on loading
loadStorage(); changeLang( lang );
// EVENT LISTENERS
pc_ram_scroller.addEventListener("scroll", shrinkTableHead);
pc_cu_scroller.addEventListener("scroll", shrinkTableHead);
// Recieve Elm updates via ports
app.ports.sendUUpdate.subscribe( (message) => {
// console.log("Update: ", message);
scrollToCurrent();
// Make sure that even when the calculation takes longer it will scroll correctly
setTimeout( scrollToCurrent, 100 );
} );
// Recieve LocalSession Updates via Ports
app.ports.localStorageSend.subscribe( (message) => {
console.log("localSessionSend: ", message);
localStorage.setItem("pc_data", message);
} );

View File

@ -14,8 +14,10 @@ import Html exposing (address)
import Json.Encode as JE import Json.Encode as JE
import Json.Decode as JD import Json.Decode as JD
import Html.Events exposing (targetValue) import Html.Events exposing (targetValue)
import Http
modelVersion = 1 modelVersion = 1
examplesListUrl = "examples-list.json"
-- Note that general Stuff is at the end of the document -- Note that general Stuff is at the end of the document
-- PORTS -- PORTS
@ -45,8 +47,21 @@ type alias Model =
{ pc : PC { pc : PC
, uCode : List UAction , uCode : List UAction
, autoscroll : Bool , autoscroll : Bool
, examples : List Example
, exampleChooserOpen : Bool
, examplesListStatus : LoaderState
, exampleLoaderStatus : LoaderState
} }
type alias Example =
{ title : String
, version : String
, url : String
, enabled : Bool
}
type LoaderState = Failure String | Waiting | Success
type UAction type UAction
= ActAccumulator2DataBus = ActAccumulator2DataBus
@ -59,12 +74,12 @@ type UAction
| ActInstructionReg2ProgrammCounter | ActInstructionReg2ProgrammCounter
| ActInstructionReg2UCounter | ActInstructionReg2UCounter
| ActProgrammCounterIncrement | ActProgrammCounterIncrement
| ActInstructionReg2ProgrammCounterIfAccEq0
| ActRam2DataBus | ActRam2DataBus
| ActResetUCounter | ActResetUCounter
| ActProgrammCounter2AddressBus | ActProgrammCounter2AddressBus
| ActNothing | ActNothing
uCodeDescriptions : List ( UAction, String ) uCodeDescriptions : List ( UAction, String )
uCodeDescriptions = uCodeDescriptions =
[ ( ActAccumulator2DataBus, "Acc -> DataBus" ) [ ( ActAccumulator2DataBus, "Acc -> DataBus" )
@ -77,6 +92,7 @@ uCodeDescriptions =
, ( ActInstructionReg2ProgrammCounter, "InstReg -> ProgCount" ) , ( ActInstructionReg2ProgrammCounter, "InstReg -> ProgCount" )
, ( ActInstructionReg2UCounter, "InstReg -> µCounter" ) , ( ActInstructionReg2UCounter, "InstReg -> µCounter" )
, ( ActProgrammCounterIncrement, "ProgCounter ++" ) , ( ActProgrammCounterIncrement, "ProgCounter ++" )
, ( ActInstructionReg2ProgrammCounterIfAccEq0, "Acc == 0 => InstReg -> ProgCounter")
, ( ActRam2DataBus, "Ram -> DataBus" ) , ( ActRam2DataBus, "Ram -> DataBus" )
, ( ActResetUCounter, "µCounter = 0" ) , ( ActResetUCounter, "µCounter = 0" )
, ( ActProgrammCounter2AddressBus, "ProgCounter -> AddrBus" ) , ( ActProgrammCounter2AddressBus, "ProgCounter -> AddrBus" )
@ -95,6 +111,7 @@ uCodeIds =
, ( ActInstructionReg2ProgrammCounter, "ir2pc" ) , ( ActInstructionReg2ProgrammCounter, "ir2pc" )
, ( ActInstructionReg2UCounter, "ir2uc" ) , ( ActInstructionReg2UCounter, "ir2uc" )
, ( ActProgrammCounterIncrement, "pcInc" ) , ( ActProgrammCounterIncrement, "pcInc" )
, ( ActInstructionReg2ProgrammCounterIfAccEq0, "ir2pciacceq0")
, ( ActRam2DataBus, "ram2db" ) , ( ActRam2DataBus, "ram2db" )
, ( ActResetUCounter, "ucReset" ) , ( ActResetUCounter, "ucReset" )
, ( ActProgrammCounter2AddressBus, "pc2ab" ) , ( ActProgrammCounter2AddressBus, "pc2ab" )
@ -113,6 +130,7 @@ uCodeMaps =
, ( ActInstructionReg2ProgrammCounter, actInstructionReg2ProgrammCounter ) , ( ActInstructionReg2ProgrammCounter, actInstructionReg2ProgrammCounter )
, ( ActInstructionReg2UCounter, actInstructionReg2UCounter ) , ( ActInstructionReg2UCounter, actInstructionReg2UCounter )
, ( ActProgrammCounterIncrement, actProgrammCounterIncrement ) , ( ActProgrammCounterIncrement, actProgrammCounterIncrement )
, ( ActInstructionReg2ProgrammCounterIfAccEq0, actInstructionReg2ProgrammCounterIfAccEq0 )
, ( ActRam2DataBus, actRam2DataBus ) , ( ActRam2DataBus, actRam2DataBus )
, ( ActResetUCounter, actResetUCounter ) , ( ActResetUCounter, actResetUCounter )
, ( ActProgrammCounter2AddressBus, actProgrammCounter2AddressBus ) , ( ActProgrammCounter2AddressBus, actProgrammCounter2AddressBus )
@ -222,7 +240,8 @@ type Msg
| MsgRamAddBelow | MsgRamAddBelow
| MsgCuEditAction Int String | MsgCuEditAction Int String
| MsgCuAddBelow | MsgCuAddBelow
| MsgCuInstrRegEdit String | MsgCuInstrRegEditAddr String
| MsgCuInstrRegEditInstr String
| MsgCuUCounterEdit String | MsgCuUCounterEdit String
| MsgCuProgCounterEdit String | MsgCuProgCounterEdit String
| MsgEditAddressBus String | MsgEditAddressBus String
@ -230,6 +249,11 @@ type Msg
| MsgAluEdit String | MsgAluEdit String
| MsgLocalSessionExport | MsgLocalSessionExport
| MsgLocalSessionRecieve String | MsgLocalSessionRecieve String
| MsgToggleLoadExample
| MsgLoadExamplesList
| MsgLoadExample Int
| MsgLoadExampleArrived (Result Http.Error Model)
| MsgExamplesArrived (Result Http.Error (List Example))
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Cmd Msg )
@ -246,16 +270,22 @@ update msg model =
) )
MsgInstructionStep -> MsgInstructionStep ->
( model, Cmd.none ) ( executeInstruction model
, cmdSenduUpdate model )
MsgReset -> MsgReset ->
updateModel { model | pc = { initialPC | ram = model.pc.ram } } let
new_model = { model | pc = { initialPC | ram = model.pc.ram } }
in
( new_model, cmdSenduUpdate new_model )
MsgManualStep action -> MsgManualStep action ->
let let
instruction = getAction action instruction = getAction action
new_model = { model | pc = instruction model.pc }
in in
updateModel { model | pc = instruction model.pc} ( new_model, cmdSenduUpdate new_model )
MsgRamEditAddress addr may_int -> MsgRamEditAddress addr may_int ->
case String.toInt may_int of case String.toInt may_int of
@ -300,11 +330,22 @@ update msg model =
MsgCuAddBelow -> MsgCuAddBelow ->
updateModel {model | uCode = model.uCode ++ [ ActNothing ]} updateModel {model | uCode = model.uCode ++ [ ActNothing ]}
MsgCuInstrRegEdit text -> MsgCuInstrRegEditAddr text ->
case String.toInt text of case String.toInt text of
Just int -> Just int ->
let old_pc = model.pc let old_pc = model.pc
new_pc = { old_pc | instructionReg = int } (instr,_) = seperateInstructionsEntry old_pc.instructionReg
new_pc = { old_pc | instructionReg = instr * 100000 + int }
in
updateModel { model | pc = new_pc }
_ -> ( model, Cmd.none )
MsgCuInstrRegEditInstr text ->
case String.toInt text of
Just int ->
let old_pc = model.pc
(_,addr) = seperateInstructionsEntry old_pc.instructionReg
new_pc = { old_pc | instructionReg = int * 100000 + addr }
in in
updateModel { model | pc = new_pc } updateModel { model | pc = new_pc }
_ -> ( model, Cmd.none ) _ -> ( model, Cmd.none )
@ -355,19 +396,65 @@ update msg model =
_ -> ( model, Cmd.none ) _ -> ( model, Cmd.none )
MsgAutoscrollUpdate -> MsgAutoscrollUpdate ->
updateModel { model | autoscroll = not model.autoscroll } if model.autoscroll then
updateModel { model | autoscroll = False }
else
let
new_model = { model | autoscroll = True }
in
( new_model
, cmdSenduUpdate new_model )
MsgLocalSessionExport -> MsgLocalSessionExport ->
( model, cmdUpdateLocalStorage model ) ( model, cmdUpdateLocalStorage model )
MsgLocalSessionRecieve message_in -> MsgLocalSessionRecieve message_in ->
case decodeModel message_in of case decodeModel model message_in of
Just new_model -> Just new_model ->
( new_model , Cmd.none ) ( { new_model | examples = model.examples } , Cmd.none )
_ -> ( model, Cmd.none ) _ -> ( model, Cmd.none )
MsgToggleLoadExample ->
if model.exampleChooserOpen then
( { model | exampleChooserOpen = False }
, Cmd.none )
else
( { model | exampleChooserOpen = True }
, cmdLoadExamples model)
MsgLoadExamplesList ->
( model, Cmd.none )
MsgLoadExample i ->
case valueAt i model.examples of
Just example ->
({ model | exampleChooserOpen = False }
, cmdLoadExampleSing model example )
Nothing ->
( model, Cmd.none )
MsgLoadExampleArrived result ->
case result of
Ok new_model ->
updateModel { model | pc = new_model.pc
, uCode = new_model.uCode
, exampleLoaderStatus = Success }
Err err ->
( { model | exampleLoaderStatus = Failure <| httpError2String err }
, Cmd.none )
MsgExamplesArrived result ->
case result of
Ok exampleList ->
( { model | examples = exampleList, examplesListStatus = Success }
, Cmd.none )
Err err ->
( { model | examplesListStatus = Failure <| httpError2String err }
, Cmd.none )
-- Practically a part of uStepPC but sepeated for manual mode -- Practically a part of uStepPC but sepeated for manual mode
getAction : UAction -> (PC -> PC) getAction : UAction -> (PC -> PC)
getAction uAction = getAction uAction =
@ -423,6 +510,16 @@ uStepPC model =
model model
executeInstruction : Model -> Model
executeInstruction model =
let
new_model = uStepPC model
in
if new_model.pc.uCounter == 0 then
uStepPC new_model
else
executeInstruction new_model
encodeModel : Model -> String encodeModel : Model -> String
encodeModel model = encodeModel model =
JE.object JE.object
@ -443,41 +540,53 @@ encodeModel model =
] ]
|> JE.encode 0 |> JE.encode 0
decodeModel : Model -> String -> Maybe Model
decodeModel : String -> Maybe Model decodeModel model text =
decodeModel text =
let let
error2maybe err = error2maybe err =
case err of case err of
Ok val -> Just val Ok val -> Just val
_ -> Nothing _ -> Nothing
uCodeDecoder =
JD.map
(\s -> Maybe.withDefault ActNothing <| string2uAction s )
(JD.string)
in in
text text
|> JD.decodeString |> JD.decodeString modelDecoder
( JD.map3
Model
( JD.field "pc"
( JD.map7
PC
(JD.field "ram" (JD.list JD.int))
(JD.field "dataBus" JD.int)
(JD.field "addressBus" JD.int)
(JD.field "instructionReg" JD.int)
(JD.field "programmCounter" JD.int)
(JD.field "uCounter" JD.int)
(JD.field "accumulator" JD.int)
)
)
( JD.field "uCode" (JD.list uCodeDecoder) )
( JD.field "autoscroll" JD.bool )
)
|> error2maybe |> error2maybe
exampleListDecoder : JD.Decoder (List Example)
exampleListDecoder =
JD.field "available"
<| JD.list
<| JD.map4
Example
( JD.field "title" JD.string )
( JD.field "version" JD.string )
( JD.field "url" JD.string )
( JD.field "enabled" (JD.map (\s -> s == 1) JD.int) )
modelDecoder : JD.Decoder Model
modelDecoder =
JD.map3
(\a b c -> Model a b c [] False Waiting Waiting)
( JD.field "pc"
( JD.map7
PC
(JD.field "ram" (JD.list JD.int))
(JD.field "dataBus" JD.int)
(JD.field "addressBus" JD.int)
(JD.field "instructionReg" JD.int)
(JD.field "programmCounter" JD.int)
(JD.field "uCounter" JD.int)
(JD.field "accumulator" JD.int)
)
)
( JD.field "uCode"
<| JD.list
<| JD.map (\s -> Maybe.withDefault ActNothing <| string2uAction s )
<| JD.string
)
( JD.field "autoscroll" JD.bool )
cmdUpdateLocalStorage : Model -> Cmd Msg cmdUpdateLocalStorage : Model -> Cmd Msg
cmdUpdateLocalStorage model = cmdUpdateLocalStorage model =
localStorageSend (encodeModel model) localStorageSend (encodeModel model)
@ -489,6 +598,30 @@ cmdSenduUpdate model =
, sendUUpdate "update" , sendUUpdate "update"
] ]
cmdLoadExamples : Model -> Cmd Msg
cmdLoadExamples model =
Http.get
{ url = examplesListUrl
, expect = Http.expectJson MsgExamplesArrived exampleListDecoder
}
cmdLoadExampleSing : Model -> Example -> Cmd Msg
cmdLoadExampleSing model example =
Http.get
{ url = example.url
, expect = Http.expectJson MsgLoadExampleArrived modelDecoder
}
httpError2String : Http.Error -> String
httpError2String err =
case err of
Http.BadUrl str -> "Bad Url: " ++ str
Http.Timeout -> "Timeout"
Http.NetworkError -> "Network Error"
Http.BadStatus num -> "Bad Status: " ++ String.fromInt num
Http.BadBody str -> "Bad Body: " ++ str
-- VIEWERS -- VIEWERS
view : Model -> Html Msg view : Model -> Html Msg
@ -501,7 +634,7 @@ viewPC model =
div div
[ class "pc" ] [ class "pc" ]
[ div [ class "controls", class "grid-fullwidth" ] [ div [ class "controls", class "grid-fullwidth" ]
[ button [ onClick MsgUCycleStep ] [ text "µZyklus" ] [ button [ onClick MsgUCycleStep ] [ text "µCycle" ]
, button [ onClick MsgInstructionStep ] [ text "Instruction" ] , button [ onClick MsgInstructionStep ] [ text "Instruction" ]
, button [ onClick MsgReset ] [ text "Reset PC" ] , button [ onClick MsgReset ] [ text "Reset PC" ]
, div , div
@ -522,13 +655,15 @@ viewPC model =
[ HAttr.for "enableScrolling" ] [ HAttr.for "enableScrolling" ]
[ text "Autoscroll" ] [ text "Autoscroll" ]
] ]
, button [ onClick MsgLocalSessionExport ] [ text "Export" ] , button [ onClick MsgToggleLoadExample ] [ text "Load Example" ]
] ]
, div [ class "grid-fullwidth" ] [ lazy viewAddressBus model ] , div [ class "grid-fullwidth" ] [ lazy viewAddressBus model ]
, lazy viewRam model , lazy viewRam model
, lazy viewCu model , lazy viewCu model
, lazy viewAlu model , lazy viewAlu model
, div [ class "grid-fullwidth" ] [ lazy viewDataBus model ] , div [ class "grid-fullwidth" ] [ lazy viewDataBus model ]
, lazy viewExamples model
, lazy viewExamplesLoaderError model
] ]
@ -618,6 +753,9 @@ viewRamContent model =
viewCu : Model -> Html Msg viewCu : Model -> Html Msg
viewCu model = viewCu model =
let
( instrRegInst, instrRegAddr ) = seperateInstructionsEntry model.pc.instructionReg
in
div [ class "section", class "cu" ] div [ class "section", class "cu" ]
[ div [ class "arrow", class "up", class "top"] [ div [ class "arrow", class "up", class "top"]
[ div [ class "button" ] [ div [ class "button" ]
@ -633,6 +771,9 @@ viewCu model =
, p [] , p []
[ div [class "input-row"] [ div [class "input-row"]
[ Html.label [ HAttr.for "cu-progcounter" ] [ text "Programm Counter:" ] [ Html.label [ HAttr.for "cu-progcounter" ] [ text "Programm Counter:" ]
, button
[ onClick <| MsgManualStep ActInstructionReg2ProgrammCounter ]
[ text "InstRA -> " ]
, Html.input , Html.input
[ HAttr.type_ "number" [ HAttr.type_ "number"
, HAttr.id "cu-progcounter" , HAttr.id "cu-progcounter"
@ -643,16 +784,30 @@ viewCu model =
, div [class "input-row"] , div [class "input-row"]
[ Html.label [ HAttr.for "cu-instrReg" ] [ text "Instruction Register:" ] [ Html.label [ HAttr.for "cu-instrReg" ] [ text "Instruction Register:" ]
, Html.input , div []
[ HAttr.type_ "number" [ Html.input
, HAttr.id "cu-instrReg" [ HAttr.type_ "number"
, value (addLeadingZero model.pc.instructionReg 8) , HAttr.id "cu-instrReg"
, onInput MsgCuInstrRegEdit , class "instruction"
] [] , value (addLeadingZero instrRegInst 3)
, onInput MsgCuInstrRegEditInstr
] []
, Html.input
[ HAttr.type_ "number"
, HAttr.id "cu-instrReg"
, class "address"
, value (addLeadingZero instrRegAddr 5)
, onInput MsgCuInstrRegEditAddr
] []
]
] ]
, div [class "input-row"] , div [class "input-row"]
[ Html.label [ HAttr.for "cu-uCounter" ] [ text "µCode Counter:" ] [ Html.label [ HAttr.for "cu-uCounter" ] [ text "µCode Counter:" ]
, button
[ onClick <| MsgManualStep ActInstructionReg2UCounter
]
[ text "IntrRI ->" ]
, Html.input , Html.input
[ HAttr.type_ "number" [ HAttr.type_ "number"
, HAttr.id "cu-uCounter" , HAttr.id "cu-uCounter"
@ -678,8 +833,8 @@ viewCuUCode model =
Html.table [] Html.table []
[ Html.thead [ class "head" ] [ Html.thead [ class "head" ]
[ Html.tr [] [ Html.tr []
[ Html.th [ class "address" ] [ text "Addr" ] [ Html.th [ class "address" ] [ text "µCounter" ]
, Html.th [] [ text "Code" ] , Html.th [] [ text "µCode" ]
] ]
] ]
, lazy viewCuUCodeContent model , lazy viewCuUCodeContent model
@ -749,7 +904,10 @@ viewAlu model =
, value (addLeadingZero model.pc.accumulator 8) , value (addLeadingZero model.pc.accumulator 8)
, onInput MsgAluEdit , onInput MsgAluEdit
] [] ] []
]
, p []
[ button [ onClick <| MsgManualStep ActAccumulatorIncrement ] [ text "Acc ++" ]
, button [ onClick <| MsgManualStep ActAccumulatorDecrement ] [ text "Acc --" ]
] ]
, div [ class "arrow", class "up"] , div [ class "arrow", class "up"]
[ div [ class "button" ] [ div [ class "button" ]
@ -800,8 +958,50 @@ viewInstrEntry i =
text ( (addLeadingZero instruction 3) ++ " " ++ (addLeadingZero address 5) ) text ( (addLeadingZero instruction 3) ++ " " ++ (addLeadingZero address 5) )
viewExamples : Model -> Html Msg
viewExamples model =
div [ classList [("modal", True), ("hidden", (not model.exampleChooserOpen))] ]
[ div [] []
, div [ class "modal-card" ]
[ Html.a [ class "modal-close", onClick MsgToggleLoadExample ] []
, lazy viewExamplesEntrys model
]
]
viewExamplesEntrys : Model -> Html Msg
viewExamplesEntrys model =
let
entry2html (id, example) =
Html.tr []
[ Html.td [] [ text <| "Example: " ++ example.title ]
, Html.td [] [ button [ onClick <| MsgLoadExample id ] [ text "Laden" ] ]
]
in
case model.examplesListStatus of
Failure msg ->
div [] [ text <| "That didn't work: " ++ msg]
Waiting ->
div [] [ text "Loading..." ]
Success ->
div [ class "scroller" ]
[ Html.table []
<| List.map entry2html
<| List.indexedMap Tuple.pair model.examples
]
viewExamplesLoaderError : Model -> Html Msg
viewExamplesLoaderError model =
case model.exampleLoaderStatus of
Failure msg ->
div [ class "modal" ]
[ div [] []
, div [ class "modal-card" ]
[ Html.p [] [ text "Something didn't work. Please refresh the page"]
, Html.p [] [ text msg ]
]
]
_ -> text ""
-- END VIEWERS -- END VIEWERS
-- ACTIONS -- ACTIONS
@ -864,6 +1064,15 @@ actProgrammCounter2AddressBus : PC -> PC
actProgrammCounter2AddressBus pc = actProgrammCounter2AddressBus pc =
{ pc | addressBus = pc.programmCounter } { pc | addressBus = pc.programmCounter }
actInstructionReg2ProgrammCounterIfAccEq0 : PC -> PC
actInstructionReg2ProgrammCounterIfAccEq0 pc =
if pc.accumulator == 0 then
let
(_,addr) = seperateInstructionsEntry pc.instructionReg
in
{ pc | programmCounter = addr }
else
pc
actDataBus2Accumulator : PC -> PC actDataBus2Accumulator : PC -> PC
actDataBus2Accumulator pc = actDataBus2Accumulator pc =
@ -1013,7 +1222,7 @@ subscriptions model =
init : () -> ( Model, Cmd Msg ) init : () -> ( Model, Cmd Msg )
init flags = init flags =
( Model initialPC initialUCodes True, Cmd.none ) ( Model initialPC initialUCodes True [] False Waiting Waiting, Cmd.none )
main : Program () Model Msg main : Program () Model Msg