aboutsummaryrefslogtreecommitdiff
path: root/demo
diff options
context:
space:
mode:
authorUri Shaked2019-11-21 19:40:02 +0200
committerUri Shaked2019-11-21 20:05:23 +0200
commitb9dfd552a62a46449532d49adc0773589076c808 (patch)
tree8eb1ec1f49e7b8e097a51ee6bf266a609eafac43 /demo
parentchore: release 0.2.0 (diff)
downloadavr8js-b9dfd552a62a46449532d49adc0773589076c808.tar.gz
avr8js-b9dfd552a62a46449532d49adc0773589076c808.tar.bz2
avr8js-b9dfd552a62a46449532d49adc0773589076c808.zip
feat: add blink demo
Diffstat (limited to 'demo')
-rw-r--r--demo/.gitignore2
-rw-r--r--demo/src/compile.ts20
-rw-r--r--demo/src/execute.ts38
-rw-r--r--demo/src/format-time.ts14
-rw-r--r--demo/src/index.css48
-rw-r--r--demo/src/index.html28
-rw-r--r--demo/src/index.ts105
-rw-r--r--demo/src/intelhex.ts18
-rw-r--r--demo/src/led.ts102
-rw-r--r--demo/tsconfig.json12
10 files changed, 387 insertions, 0 deletions
diff --git a/demo/.gitignore b/demo/.gitignore
new file mode 100644
index 0000000..1b823a2
--- /dev/null
+++ b/demo/.gitignore
@@ -0,0 +1,2 @@
+dist
+build
diff --git a/demo/src/compile.ts b/demo/src/compile.ts
new file mode 100644
index 0000000..4fca6c6
--- /dev/null
+++ b/demo/src/compile.ts
@@ -0,0 +1,20 @@
+const url = 'https://wokwi-hexi-73miufol2q-uc.a.run.app';
+
+export interface IHexiResult {
+ stdout: string;
+ stderr: string;
+ hex: string;
+}
+
+export async function buildHex(source: string) {
+ const resp = await fetch(url + '/build', {
+ method: 'POST',
+ mode: 'cors',
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ sketch: source })
+ });
+ return (await resp.json()) as IHexiResult;
+}
diff --git a/demo/src/execute.ts b/demo/src/execute.ts
new file mode 100644
index 0000000..96a0411
--- /dev/null
+++ b/demo/src/execute.ts
@@ -0,0 +1,38 @@
+import { avrInstruction, AVRTimer, CPU, timer0Config } from 'avr8js';
+import { loadHex } from './intelhex';
+
+// ATmega328p params
+const FLASH = 0x8000;
+
+export class AVRRunner {
+ readonly program = new Uint16Array(FLASH);
+ readonly cpu: CPU;
+ readonly timer: AVRTimer;
+
+ private stopped = false;
+
+ constructor(hex: string) {
+ loadHex(hex, new Uint8Array(this.program.buffer));
+ this.cpu = new CPU(this.program);
+ this.timer = new AVRTimer(this.cpu, timer0Config);
+ }
+
+ async execute(callback: (cpu: CPU) => void) {
+ this.stopped = false;
+ for (;;) {
+ avrInstruction(this.cpu);
+ this.timer.tick();
+ if (this.cpu.cycles % 50000 === 0) {
+ callback(this.cpu);
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ if (this.stopped) {
+ break;
+ }
+ }
+ }
+ }
+
+ stop() {
+ this.stopped = true;
+ }
+}
diff --git a/demo/src/format-time.ts b/demo/src/format-time.ts
new file mode 100644
index 0000000..a82b3b0
--- /dev/null
+++ b/demo/src/format-time.ts
@@ -0,0 +1,14 @@
+function zeroPad(value: number, length: number) {
+ let sval = value.toString();
+ while (sval.length < length) {
+ sval = '0' + sval;
+ }
+ return sval;
+}
+
+export function formatTime(seconds: number) {
+ const ms = Math.floor(seconds * 1000) % 1000;
+ const secs = Math.floor(seconds % 60);
+ const mins = Math.floor(seconds / 60);
+ return `${zeroPad(mins, 2)}:${zeroPad(secs, 2)}.${zeroPad(ms, 3)}`;
+}
diff --git a/demo/src/index.css b/demo/src/index.css
new file mode 100644
index 0000000..a3fa8b8
--- /dev/null
+++ b/demo/src/index.css
@@ -0,0 +1,48 @@
+body {
+ padding: 0 16px;
+ font-family: 'Roboto', sans-serif;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.app-container {
+ width: 500px;
+ max-width: 100%;
+}
+
+.toolbar {
+ padding: 4px;
+ display: flex;
+ background-color: #ddd;
+ box-sizing: border-box;
+ width: 100%;
+}
+
+.toolbar > button {
+ margin-right: 4px;
+}
+
+.spacer {
+ flex: 1;
+}
+
+.code-editor {
+ width: 100%;
+ max-width: 100%;
+ height: 300px;
+ box-sizing: border-box;
+ border: 1px solid grey;
+}
+
+.compiler-output {
+ width: 500px;
+ box-sizing: border-box;
+ padding: 8px 12px;
+ max-height: 120px;
+ overflow: auto;
+}
+
+.compiler-output pre {
+ margin: 0;
+ white-space: pre-line;
+}
diff --git a/demo/src/index.html b/demo/src/index.html
new file mode 100644
index 0000000..935bcff
--- /dev/null
+++ b/demo/src/index.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="X-UA-Compatible" content="ie=edge" />
+ <title>AVR8js LED Demo</title>
+ <link href="//fonts.googleapis.com/css?family=Roboto&display=swap" rel="stylesheet" />
+ </head>
+ <body>
+ <h2>AVR8js LED Demo</h2>
+ <div class="app-container">
+ <div class="leds"></div>
+ <div class="toolbar">
+ <button id="run-button">Run</button>
+ <button id="stop-button" disabled>Stop</button>
+ <div class="spacer"></div>
+ <div id="status-label"></div>
+ </div>
+ <div class="code-editor"></div>
+ <div class="compiler-output">
+ <pre id="compiler-output-text"></pre>
+ </div>
+ </div>
+ <script src="//cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.18.0/min/vs/loader.js"></script>
+ <script src="./index.ts"></script>
+ </body>
+</html>
diff --git a/demo/src/index.ts b/demo/src/index.ts
new file mode 100644
index 0000000..22e8143
--- /dev/null
+++ b/demo/src/index.ts
@@ -0,0 +1,105 @@
+import { buildHex } from './compile';
+import './index.css';
+import { AVRRunner } from './execute';
+import { formatTime } from './format-time';
+import { LED } from './led';
+
+let editor: any;
+const BLINK_CODE = `
+// Green LED connected to LED_BUILTIN,
+// Red LED connected to pin 12. Enjoy!
+
+void setup() {
+ pinMode(LED_BUILTIN, OUTPUT);
+}
+
+void loop() {
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(500);
+ digitalWrite(LED_BUILTIN, LOW);
+ delay(500);
+}`.trim();
+
+// Load Editor
+declare var window: any;
+declare var monaco: any;
+window.require.config({
+ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.18.0/min/vs' }
+});
+window.require(['vs/editor/editor.main'], () => {
+ editor = monaco.editor.create(document.querySelector('.code-editor'), {
+ value: BLINK_CODE,
+ language: 'cpp',
+ minimap: { enabled: false }
+ });
+});
+
+// Set up LEDs
+const leds = document.querySelector('.leds');
+const led13 = new LED({ color: 'green', lightColor: '#80ff80' });
+const led12 = new LED({ color: 'red', lightColor: '#ff8080' });
+leds.appendChild(led13.el);
+leds.appendChild(led12.el);
+
+// Set up toolbar
+let runner: AVRRunner;
+
+const runButton = document.querySelector('#run-button');
+runButton.addEventListener('click', compileAndRun);
+const stopButton = document.querySelector('#stop-button');
+stopButton.addEventListener('click', stopCode);
+const statusLabel = document.querySelector('#status-label');
+const compilerOutputText = document.querySelector('#compiler-output-text');
+
+function executeProgram(hex: string) {
+ runner = new AVRRunner(hex);
+ const MHZ = 16000000;
+
+ // Hook to PORTB output
+ runner.cpu.writeHooks[0x25] = (value: number) => {
+ const DDRB = runner.cpu.data[0x24];
+ value &= DDRB;
+ const D12bit = 1 << 4;
+ const D13bit = 1 << 5;
+ led12.value = value & D12bit ? true : false;
+ led13.value = value & D13bit ? true : false;
+ };
+
+ runner.execute((cpu) => {
+ const time = formatTime(cpu.cycles / MHZ);
+ statusLabel.textContent = 'Simulation time: ' + time;
+ });
+}
+
+async function compileAndRun() {
+ led12.value = false;
+ led13.value = false;
+
+ runButton.setAttribute('disabled', '1');
+ try {
+ statusLabel.textContent = 'Compiling...';
+ const result = await buildHex(editor.getModel().getValue());
+ compilerOutputText.textContent = result.stderr || result.stdout;
+ if (result.hex) {
+ compilerOutputText.textContent += '\nProgram running...';
+ stopButton.removeAttribute('disabled');
+ executeProgram(result.hex);
+ } else {
+ runButton.removeAttribute('disabled');
+ }
+ } catch (err) {
+ runButton.removeAttribute('disabled');
+ alert('Failed: ' + err);
+ } finally {
+ statusLabel.textContent = '';
+ }
+}
+
+function stopCode() {
+ stopButton.setAttribute('disabled', '1');
+ runButton.removeAttribute('disabled');
+ if (runner) {
+ runner.stop();
+ runner = null;
+ }
+}
diff --git a/demo/src/intelhex.ts b/demo/src/intelhex.ts
new file mode 100644
index 0000000..ba5d6c8
--- /dev/null
+++ b/demo/src/intelhex.ts
@@ -0,0 +1,18 @@
+/**
+ * Minimal Intel HEX loader
+ * Part of AVR8js
+ *
+ * Copyright (C) 2019, Uri Shaked
+ */
+
+export function loadHex(source: string, target: Uint8Array) {
+ for (const line of source.split('\n')) {
+ if (line[0] === ':' && line.substr(7, 2) === '00') {
+ const bytes = parseInt(line.substr(1, 2), 16);
+ const addr = parseInt(line.substr(3, 4), 16);
+ for (let i = 0; i < bytes; i++) {
+ target[addr + i] = parseInt(line.substr(9 + i * 2, 2), 16);
+ }
+ }
+ }
+}
diff --git a/demo/src/led.ts b/demo/src/led.ts
new file mode 100644
index 0000000..8c6deb4
--- /dev/null
+++ b/demo/src/led.ts
@@ -0,0 +1,102 @@
+const led = `<svg
+width="40"
+height="50"
+version="1.2"
+viewBox="-10 -5 35.456 39.618"
+xmlns="http://www.w3.org/2000/svg"
+>
+<filter id="light1" x="-0.8" y="-0.8" height="2.2" width="2.8">
+ <feGaussianBlur stdDeviation="2.6" />
+</filter>
+<filter id="light2" x="-0.8" y="-0.8" height="2.2" width="2.8">
+ <feGaussianBlur stdDeviation="4" />
+</filter>
+<rect x="3.451" y="19.379" width="2.1514" height="9.8273" fill="#8c8c8c" />
+<path
+ d="m12.608 29.618c0-1.1736-0.86844-2.5132-1.8916-3.4024-0.41616-0.3672-1.1995-1.0015-1.1995-1.4249v-5.4706h-2.1614v5.7802c0 1.0584 0.94752 1.8785 1.9462 2.7482 0.44424 0.37584 1.3486 1.2496 1.3486 1.7694"
+ fill="#8c8c8c"
+/>
+<path
+ d="m14.173 13.001v-5.9126c0-3.9132-3.168-7.0884-7.0855-7.0884-3.9125 0-7.0877 3.1694-7.0877 7.0884v13.649c1.4738 1.651 4.0968 2.7526 7.0877 2.7526 4.6195 0 8.3686-2.6179 8.3686-5.8594v-1.5235c-7.4e-4 -1.1426-0.47444-2.2039-1.283-3.1061z"
+ opacity=".3"
+/>
+<path
+ d="m14.173 13.001v-5.9126c0-3.9132-3.168-7.0884-7.0855-7.0884-3.9125 0-7.0877 3.1694-7.0877 7.0884v13.649c1.4738 1.651 4.0968 2.7526 7.0877 2.7526 4.6195 0 8.3686-2.6179 8.3686-5.8594v-1.5235c-7.4e-4 -1.1426-0.47444-2.2039-1.283-3.1061z"
+ fill="#e6e6e6"
+ opacity=".5"
+/>
+<path
+ d="m14.173 13.001v3.1054c0 2.7389-3.1658 4.9651-7.0855 4.9651-3.9125 2e-5 -7.0877-2.219-7.0877-4.9651v4.6296c1.4738 1.6517 4.0968 2.7526 7.0877 2.7526 4.6195 0 8.3686-2.6179 8.3686-5.8586l-4e-5 -1.5235c-7e-4 -1.1419-0.4744-2.2032-1.283-3.1054z"
+ fill="#d1d1d1"
+ opacity=".9"
+/>
+<g>
+ <path
+ d="m14.173 13.001v3.1054c0 2.7389-3.1658 4.9651-7.0855 4.9651-3.9125 2e-5 -7.0877-2.219-7.0877-4.9651v4.6296c1.4738 1.6517 4.0968 2.7526 7.0877 2.7526 4.6195 0 8.3686-2.6179 8.3686-5.8586l-4e-5 -1.5235c-7e-4 -1.1419-0.4744-2.2032-1.283-3.1054z"
+ opacity=".7"
+ />
+ <path
+ d="m14.173 13.001v3.1054c0 2.7389-3.1658 4.9651-7.0855 4.9651-3.9125 2e-5 -7.0877-2.219-7.0877-4.9651v3.1054c1.4738 1.6502 4.0968 2.7526 7.0877 2.7526 4.6195 0 8.3686-2.6179 8.3686-5.8586-7.4e-4 -1.1412-0.47444-2.2025-1.283-3.1047z"
+ opacity=".25"
+ />
+ <ellipse cx="7.0877" cy="16.106" rx="7.087" ry="4.9608" opacity=".25" />
+</g>
+<polygon
+ points="2.2032 16.107 3.1961 16.107 3.1961 13.095 6.0156 13.095 10.012 8.8049 3.407 8.8049 2.2032 9.648"
+ fill="#666666"
+/>
+<polygon
+ points="11.215 9.0338 7.4117 13.095 11.06 13.095 11.06 16.107 11.974 16.107 11.974 8.5241 10.778 8.5241"
+ fill="#666666"
+/>
+<path
+ d="m14.173 13.001v-5.9126c0-3.9132-3.168-7.0884-7.0855-7.0884-3.9125 0-7.0877 3.1694-7.0877 7.0884v13.649c1.4738 1.651 4.0968 2.7526 7.0877 2.7526 4.6195 0 8.3686-2.6179 8.3686-5.8594v-1.5235c-7.4e-4 -1.1426-0.47444-2.2039-1.283-3.1061z"
+ fill="{{color}}"
+ opacity=".65"
+/>
+<g fill="#ffffff">
+ <path
+ d="m10.388 3.7541 1.4364-0.2736c-0.84168-1.1318-2.0822-1.9577-3.5417-2.2385l0.25416 1.0807c0.76388 0.27072 1.4068 0.78048 1.8511 1.4314z"
+ opacity=".5"
+ />
+ <path
+ d="m0.76824 19.926v1.5199c0.64872 0.5292 1.4335 0.97632 2.3076 1.3169v-1.525c-0.8784-0.33624-1.6567-0.78194-2.3076-1.3118z"
+ opacity=".5"
+ />
+ <path
+ d="m11.073 20.21c-0.2556 0.1224-0.52992 0.22968-0.80568 0.32976-0.05832 0.01944-0.11736 0.04032-0.17784 0.05832-0.56376 0.17928-1.1614 0.31896-1.795 0.39456-0.07488 0.0094-0.1512 0.01872-0.22464 0.01944-0.3204 0.03024-0.64368 0.05832-0.97056 0.05832-0.14832 0-0.30744-0.01512-0.4716-0.02376-1.2002-0.05688-2.3306-0.31464-3.2976-0.73944l-2e-5 -8.3895v-4.8254c0-1.471 0.84816-2.7295 2.0736-3.3494l-0.02232-0.05328-1.2478-1.512c-1.6697 1.003-2.79 2.8224-2.79 4.9118v11.905c-0.04968-0.04968-0.30816-0.30888-0.48024-0.52992l-0.30744 0.6876c1.4011 1.4818 3.8088 2.4617 6.5426 2.4617 1.6798 0 3.2371-0.37368 4.5115-1.0022l-0.52704-0.40896-0.01006 0.0072z"
+ opacity=".5"
+ />
+</g>
+<g class="light">
+ <ellipse cx="8" cy="10" rx="10" ry="10" fill="{{lightColor}}" filter="url(#light2)"></ellipse>
+ <ellipse cx="8" cy="10" rx="3" ry="3" fill="white" filter="url(#light1)"></ellipse>
+</g>
+</svg>
+`;
+
+export interface ILEDOptions {
+ color: string;
+ lightColor?: string;
+}
+
+const ON_CLASS = 'led-on';
+export class LED {
+ readonly el = document.createElement('span');
+ private readonly lightEl: SVGElement;
+ constructor({ color, lightColor }: ILEDOptions) {
+ this.el.innerHTML = led
+ .replace('{{color}}', color)
+ .replace('{{lightColor}}', lightColor || color);
+ this.lightEl = this.el.querySelector('.light');
+ this.lightEl.style.display = 'none';
+ }
+
+ get value() {
+ return this.lightEl.style.display !== 'none';
+ }
+
+ set value(value: boolean) {
+ this.lightEl.style.display = value ? '' : 'none';
+ }
+}
diff --git a/demo/tsconfig.json b/demo/tsconfig.json
new file mode 100644
index 0000000..2c2940e
--- /dev/null
+++ b/demo/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": "..",
+ "rootDir": "..",
+ "lib": ["dom"],
+ "paths": {
+ "avr8js": ["src"]
+ }
+ },
+ "include": ["fuse.ts", "src/**/*.ts"]
+}
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.