-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic assembler/lc-3 VM works on browser.
- Loading branch information
Nam Jeonghyun
committed
Sep 5, 2019
0 parents
commit 98d7120
Showing
12 changed files
with
762 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/target | ||
**/*.rs.bk | ||
Cargo.lock | ||
/.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "lc3web" | ||
version = "0.1.0" | ||
authors = ["Nam Jeonghyun <[email protected]>"] | ||
edition = "2018" | ||
|
||
publish = false | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
yew = "0.8" | ||
lc3-rs = "0.5" | ||
serde = "1.0" | ||
stdweb = "0.4" | ||
lc3asm = "0.1" |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# lc3web: [lc3-rs](https://github.com/cr0sh/lc3-rs)\/[lc3asm](https://github.com/cr0sh/lc3asm) on web browser | ||
This is a demo program about running LC-3 tools on web browser, using [WebAssembly](https://developer.mozilla.org/docs/WebAssembly) technology. | ||
|
||
Technologies/Software used: | ||
- [Rust](http://www.rust-lang.org) (>90% of codes were written + some HTML/CSS) | ||
- [WebAssembly](https://developer.mozilla.org/docs/WebAssembly) (To run on browser) | ||
- [Yew](https://github.com/yewstack/yew) (frontend) | ||
|
||
## Requirements | ||
- Rust/Cargo | ||
- [cargo-web](https://github.com/koute/cargo-web) | ||
- Install with `cargo install cargo-web` | ||
|
||
Other dependencies will be downloaded during building. | ||
|
||
## Running | ||
- `git clone` this repository | ||
- `cargo web run` | ||
- Go to http://localhost:8000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use crate::util::{CrossComponentBridge, Props}; | ||
use yew::prelude::*; | ||
use std::cell::RefCell; | ||
use std::rc::Rc; | ||
|
||
pub struct AssemblerConsole { | ||
source: String, | ||
output: String, | ||
bridge: Rc<RefCell<CrossComponentBridge<Vec<u8>>>>, | ||
} | ||
|
||
pub enum Msg { | ||
CheckSource, | ||
AssembleAndLoad, | ||
SourceInput(String), | ||
} | ||
|
||
impl Component for AssemblerConsole { | ||
type Message = Msg; | ||
type Properties = Props; | ||
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self { | ||
Self { | ||
source: String::new(), | ||
output: String::new(), | ||
bridge: props.bridge, | ||
} | ||
} | ||
|
||
fn update(&mut self, msg: Self::Message) -> ShouldRender { | ||
match msg { | ||
Msg::CheckSource => { | ||
self.output.clear(); | ||
self.output.push_str("Checking..."); | ||
match lc3asm::assemble(&self.source) { | ||
Ok(_) => { | ||
self.output.push_str("Success\n"); | ||
} | ||
Err(e) => { | ||
self.output.push_str("Failed\n"); | ||
self.output.push_str(&e.to_string()); | ||
} | ||
} | ||
true | ||
} | ||
Msg::AssembleAndLoad => { | ||
self.output.clear(); | ||
self.output.push_str("Assembling..."); | ||
match lc3asm::assemble(&self.source) { | ||
Ok((obj, _)) => { | ||
self.output.push_str("Success\n"); | ||
self.output.push_str("Sending to LC-3 console..."); | ||
self.bridge.borrow().send(obj); | ||
self.output.push_str("Done\n"); | ||
} | ||
Err(e) => { | ||
self.output.push_str("Failed\n"); | ||
self.output.push_str(&e.to_string()); | ||
} | ||
} | ||
true | ||
} | ||
Msg::SourceInput(value) => { | ||
self.source = value; | ||
true | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl Renderable<Self> for AssemblerConsole { | ||
fn view(&self) -> Html<Self> { | ||
html! { | ||
<div> | ||
{ "Source code" } | ||
<br /> | ||
<textarea id="asm-source" rows=15 cols=25 value=&self.source oninput=|e| Msg::SourceInput(e.value) /> | ||
<textarea readonly=true id="asm-output" rows=15 cols=25> | ||
{ &self.output } | ||
</textarea> | ||
<button onclick = |_| Msg::CheckSource>{ "Check" }</button> | ||
<button onclick = |_| Msg::AssembleAndLoad>{ "Assemble and load" }</button> | ||
</div> | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
use crate::util::Props; | ||
use lc3::{IOStreamHandler, VM}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::cell::Cell; | ||
use std::io::{Cursor, Read, Result as IOResult}; | ||
use yew::prelude::*; | ||
use yew::worker::*; | ||
|
||
pub struct LC3Console { | ||
display: String, | ||
input: String, | ||
context: Box<dyn Bridge<LC3Agent>>, | ||
} | ||
|
||
pub enum Msg { | ||
AgentResponse(String), | ||
LoadSample, | ||
Run, | ||
LoadFromAssembler(Vec<u8>), | ||
Clear, | ||
Input(String), | ||
} | ||
|
||
impl Component for LC3Console { | ||
type Message = Msg; | ||
type Properties = Props; | ||
fn create(props: Self::Properties, mut link: ComponentLink<Self>) -> Self { | ||
let callback = link.send_back(|x: <LC3Agent as Agent>::Output| Msg::AgentResponse(x.0)); | ||
props | ||
.bridge | ||
.borrow_mut() | ||
.register_callback(link.send_back(|v| { | ||
stdweb::console!(log, "Received program!"); | ||
Msg::LoadFromAssembler(v) | ||
})); | ||
stdweb::console!(log, "Registered CCB callback"); | ||
LC3Console { | ||
display: String::new(), | ||
input: String::new(), | ||
context: LC3Agent::bridge(callback), | ||
} | ||
} | ||
|
||
fn update(&mut self, msg: Self::Message) -> ShouldRender { | ||
match msg { | ||
Msg::LoadSample => { | ||
self.display.clear(); | ||
self.context.send(AgentRequest::Reset); | ||
self.context.send(AgentRequest::Load(Vec::from( | ||
include_bytes!("print.obj").as_ref(), | ||
))); | ||
true | ||
} | ||
Msg::Run => { | ||
self.display.clear(); | ||
self.context.send(AgentRequest::Run(self.input.clone())); | ||
false | ||
} | ||
Msg::Clear => { | ||
self.display.clear(); | ||
true | ||
} | ||
Msg::LoadFromAssembler(v) => { | ||
self.display.clear(); | ||
self.display.push_str("Loading assembled file...\n"); | ||
self.context.send(AgentRequest::Reset); | ||
self.context.send(AgentRequest::Load(v)); | ||
true | ||
} | ||
Msg::AgentResponse(s) => { | ||
self.display.push_str(&s); | ||
true | ||
} | ||
Msg::Input(value) => { | ||
self.input = value; | ||
true | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl Renderable<Self> for LC3Console { | ||
fn view(&self) -> Html<Self> { | ||
html! { | ||
<div> | ||
{ "Output" } | ||
<br /> | ||
<textarea id="lc3-output" rows=15 cols=25 value=&self.display /> | ||
<button onclick = |_| Msg::LoadSample>{ "Load sample program" }</button> | ||
<br /> | ||
{ "Input" } | ||
<br /> | ||
<textarea id="lc3-input" rows=15 cols=25 value=&self.input oninput=|e| Msg::Input(e.value) /> | ||
<button onclick = |_| Msg::Run>{ "Run" }</button> | ||
<button onclick = |_| Msg::Clear>{ "Clear output screen" }</button> | ||
</div> | ||
} | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize)] | ||
enum AgentRequest { | ||
Load(Vec<u8>), | ||
Run(String), | ||
Reset, | ||
} | ||
impl Transferable for AgentRequest {} | ||
|
||
#[derive(Serialize, Deserialize)] | ||
struct AgentResponse(String); | ||
impl Transferable for AgentResponse {} | ||
|
||
struct LC3Agent { | ||
lc3_vm: Box<VM<IOStreamHandler<StringCell, Vec<u8>>>>, | ||
link: AgentLink<Self>, | ||
} | ||
|
||
impl Agent for LC3Agent { | ||
type Reach = Context; | ||
type Message = (); | ||
type Input = AgentRequest; | ||
type Output = AgentResponse; | ||
|
||
fn create(link: AgentLink<Self>) -> Self { | ||
let vm = VM::new(( | ||
StringCell(Cell::new(Cursor::new(String::new()))), | ||
Vec::new(), | ||
)); | ||
Self { | ||
lc3_vm: Box::new(vm), | ||
link, | ||
} | ||
} | ||
|
||
fn update(&mut self, _msg: Self::Message) {} | ||
|
||
fn handle(&mut self, msg: Self::Input, who: HandlerId) { | ||
match msg { | ||
AgentRequest::Load(program) => { | ||
self.lc3_vm.load_u8(&program); | ||
self.link | ||
.response(who, AgentResponse(String::from("Loaded program...\n"))) | ||
} | ||
AgentRequest::Run(input) => { | ||
(self.lc3_vm.context.0).0.set(Cursor::new(input)); | ||
self.lc3_vm.run().unwrap(); | ||
self.link.response( | ||
who, | ||
AgentResponse(String::from_utf8_lossy(&self.lc3_vm.context.1).into_owned()), | ||
) | ||
} | ||
AgentRequest::Reset => { | ||
let vm = VM::new(( | ||
StringCell(Cell::new(Cursor::new(String::new()))), | ||
Vec::new(), | ||
)); | ||
self.lc3_vm = Box::new(vm); | ||
self.link.response(who, AgentResponse(String::from("VM reset complete\n"))) | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct StringCell(Cell<Cursor<String>>); | ||
|
||
impl Read for StringCell { | ||
fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> { | ||
self.0.get_mut().read(buf) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#![recursion_limit = "512"] | ||
|
||
pub mod assembler; | ||
pub mod lc3; | ||
pub mod util; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
use lc3web::assembler::AssemblerConsole; | ||
use lc3web::lc3::LC3Console; | ||
use lc3web::util::CrossComponentBridge; | ||
use yew::prelude::*; | ||
use std::rc::Rc; | ||
use std::cell::RefCell; | ||
|
||
struct RootModel(Rc<RefCell<CrossComponentBridge<Vec<u8>>>>); | ||
|
||
impl Component for RootModel { | ||
type Message = (); | ||
type Properties = (); | ||
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self { | ||
Self(Rc::new(RefCell::new(CrossComponentBridge::new()))) | ||
} | ||
|
||
fn update(&mut self, _: Self::Message) -> ShouldRender { | ||
true | ||
} | ||
} | ||
|
||
impl Renderable<RootModel> for RootModel { | ||
fn view(&self) -> Html<Self> { | ||
html! { | ||
<div id="root-element"> | ||
<div class="outer-container"> | ||
<label>{"LC-3 Console"}</label> | ||
<div class="container"> | ||
<LC3Console bridge=Rc::clone(&self.0) /> | ||
</div> | ||
</div> | ||
<div class="outer-container"> | ||
<label>{"Assembler Console"}</label> | ||
<div class="container"> | ||
<AssemblerConsole bridge=Rc::clone(&self.0) /> | ||
</div> | ||
</div> | ||
</div> | ||
} | ||
} | ||
} | ||
|
||
fn main() { | ||
yew::start_app::<RootModel>() | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use std::rc::Rc; | ||
use yew::prelude::*; | ||
use std::cell::RefCell; | ||
|
||
#[derive(Properties)] | ||
pub struct Props { | ||
#[props(required)] | ||
pub bridge: Rc<RefCell<CrossComponentBridge<Vec<u8>>>>, | ||
} | ||
|
||
pub struct CrossComponentBridge<T> { | ||
callback: Option<Callback<T>>, | ||
} | ||
|
||
impl<T> CrossComponentBridge<T> { | ||
pub fn new() -> Self { | ||
Self { callback: None } | ||
} | ||
pub fn send(&self, msg: T) { | ||
if let Some(cb) = &self.callback { | ||
cb.emit(msg) | ||
} else { | ||
stdweb::console!(log, "?"); | ||
} | ||
} | ||
pub fn register_callback(&mut self, callback: Callback<T>) { | ||
self.callback = Some(callback) | ||
} | ||
} |
Oops, something went wrong.