diff --git a/crsn/src/asm/instr/flatten.rs b/crsn/src/asm/instr/flatten.rs index da6436f..3fc3bab 100644 --- a/crsn/src/asm/instr/flatten.rs +++ b/crsn/src/asm/instr/flatten.rs @@ -9,11 +9,11 @@ use crate::builtin::defs::BuiltinOp; /// A trait for something that can turn into multiple instructions pub trait Flatten { - fn flatten(self, label_num: &AtomicU32) -> Result, CrsnError>; + fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError>; } impl Flatten for Instr { - fn flatten(self, label_num: &AtomicU32) -> Result, CrsnError> { + fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError> { let mut ops = vec![self.op]; if let Some(branches) = self.branches { @@ -31,10 +31,7 @@ impl Flatten for Instr { Label::unique(label_num) }; ops.push(BuiltinOp::JumpIf(!cond, next_lbl.clone()).into()); - - for branch_instr in branch { - ops.extend(branch_instr.flatten(label_num)?); - } + ops.extend(branch.flatten(label_num)?); if cnt != branch_count - 1 { ops.push(BuiltinOp::Jump(end_lbl.clone()).into()); @@ -48,15 +45,23 @@ impl Flatten for Instr { } } +impl Flatten for Vec> { + fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError> { + let mut ops = vec![]; + for item in self.into_iter() { + ops.extend(item.flatten(label_num)?); + } + Ok(ops) + } +} + impl Flatten for Routine { - fn flatten(self, label_num: &AtomicU32) -> Result, CrsnError> { + fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError> { let mut ops = vec![ BuiltinOp::Routine(self.name.clone()).into(), ]; - for instr in self.body { - ops.extend(instr.flatten(label_num)?); - } + ops.extend(self.body.flatten(label_num)?); ops.push(BuiltinOp::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into())).into()); diff --git a/crsn/src/asm/instr/mod.rs b/crsn/src/asm/instr/mod.rs index f1f7ead..94f25a5 100644 --- a/crsn/src/asm/instr/mod.rs +++ b/crsn/src/asm/instr/mod.rs @@ -9,15 +9,13 @@ pub mod cond; mod flatten; /// A higher-level instruction -#[derive(Debug)] pub struct Instr { pub op: Op, - pub branches: Option)>>, + pub branches: Option)>>, } /// A routine pub struct Routine { pub name: RoutineName, - pub body: Vec, + pub body: Box, } - diff --git a/crsn/src/asm/parse/arg_parser.rs b/crsn/src/asm/parse/arg_parser.rs index cc14fa2..111b7e1 100644 --- a/crsn/src/asm/parse/arg_parser.rs +++ b/crsn/src/asm/parse/arg_parser.rs @@ -5,21 +5,22 @@ use crate::asm::parse::parse_data::{parse_rd, parse_wr}; use crate::asm::parse::sexp_expect::expect_string_atom; /// Utility for argument parsing -pub struct ArgParser { +pub struct TokenParser { orig_len: usize, args: Vec, } -impl IntoIterator for ArgParser { +impl IntoIterator for TokenParser { type Item = Sexp; type IntoIter = std::vec::IntoIter; - fn into_iter(self) -> Self::IntoIter { + fn into_iter(mut self) -> Self::IntoIter { + self.args.reverse(); self.args.into_iter() } } -impl ArgParser { +impl TokenParser { /// Create a new argument parser pub fn new(mut args: Vec) -> Self { args.reverse(); diff --git a/crsn/src/asm/parse/mod.rs b/crsn/src/asm/parse/mod.rs index fe08f4e..0e471e1 100644 --- a/crsn/src/asm/parse/mod.rs +++ b/crsn/src/asm/parse/mod.rs @@ -1,7 +1,6 @@ -use std::sync::atomic::AtomicU32; +use std::sync::atomic::{AtomicU32, AtomicU64}; pub use parse_instr::parse_instructions; -use parse_routines::parse_routines; use crate::asm::error::CrsnError; use crate::asm::instr::{Flatten, Op, Routine}; @@ -11,22 +10,15 @@ use crate::module::CrsnExtension; mod parse_cond; mod parse_instr; pub mod parse_data; -mod parse_routines; pub mod sexp_expect; mod parse_op; pub mod arg_parser; pub fn parse(source: &str, parsers: &[Box]) -> Result, CrsnError> { - let root = sexp::parse(source)?; + let items = expect_list(Some(sexp::parse(source)?), true)?; - let subs: Vec = parse_routines(expect_list(Some(root), true)?, parsers)?; - - let mut combined = vec![]; let label_num = AtomicU32::new(0); - for sub in subs { - combined.extend(sub.flatten(&label_num)?); - } - - Ok(combined) + parse_instructions(items.into_iter(), parsers)? + .flatten(&label_num) } diff --git a/crsn/src/asm/parse/parse_cond.rs b/crsn/src/asm/parse/parse_cond.rs index 177aa80..5089c45 100644 --- a/crsn/src/asm/parse/parse_cond.rs +++ b/crsn/src/asm/parse/parse_cond.rs @@ -1,13 +1,13 @@ use sexp::Sexp; use crate::asm::error::CrsnError; -use crate::asm::instr::{Cond, cond, Instr}; +use crate::asm::instr::{Cond, cond, Instr, Flatten}; use crate::asm::parse::parse_instr::parse_instructions; use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; use crate::asm::patches::TryRemove; use crate::module::CrsnExtension; -pub fn parse_cond_branch(tok: Sexp, parsers: &[Box]) -> Result<(Cond, Vec), CrsnError> { +pub fn parse_cond_branch(tok: Sexp, parsers: &[Box]) -> Result<(Cond, Box), CrsnError> { let mut list = expect_list(Some(tok), false)?; let kw = expect_string_atom(list.try_remove(0))?; @@ -15,6 +15,6 @@ pub fn parse_cond_branch(tok: Sexp, parsers: &[Box]) -> Resul return Err(CrsnError::Parse(format!("Condition must end with '?': {}", kw).into())); } - Ok((cond::parse_cond(&kw)?, parse_instructions(list, parsers)?)) + Ok((cond::parse_cond(&kw)?, parse_instructions(list.into_iter(), parsers)?)) } diff --git a/crsn/src/asm/parse/parse_instr.rs b/crsn/src/asm/parse/parse_instr.rs index 0025de7..8e9e184 100644 --- a/crsn/src/asm/parse/parse_instr.rs +++ b/crsn/src/asm/parse/parse_instr.rs @@ -1,24 +1,49 @@ use sexp::Sexp; use crate::asm::error::CrsnError; -use crate::asm::instr::Instr; -use crate::asm::parse::arg_parser::ArgParser; +use crate::asm::instr::{Instr, Flatten, Routine}; +use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::parse_cond::parse_cond_branch; use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; use crate::asm::patches::SexpIsA; -use crate::module::CrsnExtension; +use crate::module::{CrsnExtension, ParseRes}; use super::parse_op::parse_op; +use crate::asm::data::literal::RoutineName; -pub fn parse_instructions(instrs: Vec, parsers: &[Box]) -> Result, CrsnError> { +pub fn parse_instructions(items: impl Iterator, parsers: &[Box]) -> Result, CrsnError> { let mut parsed = vec![]; - for expr in instrs { + for expr in items { let tokens = expect_list(Some(expr), false)?; let mut toki = tokens.into_iter(); let name = expect_string_atom(toki.next())?; - let arg_tokens = ArgParser::new(toki.clone().take_while(|e| e.is_atom()).collect()); + if name == "proc" { + parsed.push(parse_routine(toki, parsers)?); + continue; + } + + let mut token_parser = TokenParser::new(toki.collect()); + for p in parsers { + token_parser = match p.parse_syntax(&name, token_parser, parsers) { + Ok(ParseRes::Parsed(op)) => return Ok(op), + Ok(ParseRes::Unknown(to_reuse)) => { + if to_reuse.parsing_started() { + panic!("Module \"{}\" started parsing syntax, but returned Unknown!", p.name()); + } + to_reuse + } + Err(err) => { + return Err(err); + } + } + } + + // Get back the original iterator + let toki = token_parser.into_iter(); + + let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect()); let branch_tokens = toki .skip_while(|e| e.is_atom()) .take_while(|e| e.is_list()); @@ -35,11 +60,25 @@ pub fn parse_instructions(instrs: Vec, parsers: &[Box]) } }; - parsed.push(Instr { + parsed.push(Box::new(Instr { op: parse_op(name.as_str(), arg_tokens, parsers)?, branches, - }); + })); } - Ok(parsed) + Ok(Box::new(parsed)) } +fn parse_routine(mut toki : impl Iterator + Clone, parsers: &[Box]) -> Result, CrsnError> { + let name = expect_string_atom(toki.next())?; + + let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect()); + + // TODO parse arg tokens + + let body = parse_instructions(toki, parsers)?; + + return Ok(Box::new(Routine { + name: RoutineName(name), + body + })) +} diff --git a/crsn/src/asm/parse/parse_op.rs b/crsn/src/asm/parse/parse_op.rs index 3b0c07d..7b64b80 100644 --- a/crsn/src/asm/parse/parse_op.rs +++ b/crsn/src/asm/parse/parse_op.rs @@ -1,17 +1,17 @@ use crate::asm::error::CrsnError; use crate::asm::instr::Op; -use crate::asm::parse::arg_parser::ArgParser; +use crate::asm::parse::arg_parser::TokenParser; use crate::builtin::parse::BuiltinOps; -use crate::module::{CrsnExtension, ParseOpRes}; +use crate::module::{CrsnExtension, ParseRes}; -pub fn parse_op(keyword: &str, mut arg_tokens: ArgParser, parsers: &[Box]) -> Result { +pub fn parse_op(keyword: &str, mut arg_tokens: TokenParser, parsers: &[Box]) -> Result { // Include built-in instructions let builtins = [BuiltinOps::new()]; for p in builtins.iter().chain(parsers) { arg_tokens = match p.parse_op(keyword, arg_tokens) { - Ok(ParseOpRes::Parsed(op)) => return Ok(op), - Ok(ParseOpRes::Unknown(to_reuse)) => { + Ok(ParseRes::Parsed(op)) => return Ok(op), + Ok(ParseRes::Unknown(to_reuse)) => { if to_reuse.parsing_started() { panic!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword); } diff --git a/crsn/src/asm/parse/parse_routines.rs b/crsn/src/asm/parse/parse_routines.rs deleted file mode 100644 index 081f169..0000000 --- a/crsn/src/asm/parse/parse_routines.rs +++ /dev/null @@ -1,23 +0,0 @@ -use sexp::Sexp; - -use crate::asm::data::literal::RoutineName; -use crate::asm::error::CrsnError; -use crate::asm::instr::Routine; -use crate::asm::parse::parse_instr::parse_instructions; -use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; -use crate::asm::patches::TryRemove; -use crate::module::CrsnExtension; - -pub fn parse_routines(routines: Vec, parsers: &[Box]) -> Result, CrsnError> { - let mut parsed = vec![]; - for rt in routines { - let mut def = expect_list(Some(rt), false)?; - let name = expect_string_atom(def.try_remove(0))?; - let body = parse_instructions(def, parsers)?; - parsed.push(Routine { - name: RoutineName(name), - body, - }) - } - Ok(parsed) -} diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index bbae512..dd1d40b 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -4,11 +4,11 @@ use crate::asm::data::literal::{Label, RoutineName}; use crate::asm::error::CrsnError; use crate::asm::instr::cond::parse_cond; use crate::asm::instr::Op; -use crate::asm::parse::arg_parser::ArgParser; +use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::parse_data::{parse_label, parse_rd}; use crate::asm::parse::sexp_expect::expect_string_atom; use crate::builtin::defs::BuiltinOp; -use crate::module::{CrsnExtension, ParseOpRes}; +use crate::module::{CrsnExtension, ParseRes}; #[derive(Debug, Clone)] pub struct BuiltinOps { @@ -28,8 +28,8 @@ impl CrsnExtension for BuiltinOps { "builtin" } - fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result, CrsnError> { - Ok(ParseOpRes::Parsed(Op::BuiltIn(match keyword { + fn parse_op(&self, keyword: &str, mut args: TokenParser) -> Result, CrsnError> { + Ok(ParseRes::Parsed(Op::BuiltIn(match keyword { "nop" => { BuiltinOp::Nop } @@ -142,10 +142,10 @@ impl CrsnExtension for BuiltinOps { let label = Label::Named(label.to_string()); BuiltinOp::FarLabel(label) } else { - return Ok(ParseOpRes::Unknown(args)); + return Ok(ParseRes::Unknown(args)); } } else { - return Ok(ParseOpRes::Unknown(args)); + return Ok(ParseRes::Unknown(args)); } } @@ -154,7 +154,7 @@ impl CrsnExtension for BuiltinOps { let label = Label::Named(label.to_string()); BuiltinOp::Label(label) } else { - return Ok(ParseOpRes::Unknown(args)); + return Ok(ParseRes::Unknown(args)); } } }))) diff --git a/crsn/src/module/mod.rs b/crsn/src/module/mod.rs index cb5f51a..473c1b8 100644 --- a/crsn/src/module/mod.rs +++ b/crsn/src/module/mod.rs @@ -1,3 +1,5 @@ +#![allow(unused_variables)] + use std::fmt::Debug; pub use eval_res::EvalRes; @@ -5,8 +7,8 @@ pub use eval_res::EvalRes; use crate::asm::data::literal::Value; use crate::asm::data::Mask; use crate::asm::error::CrsnError; -use crate::asm::instr::Op; -use crate::asm::parse::arg_parser::ArgParser; +use crate::asm::instr::{Op, Flatten}; +use crate::asm::parse::arg_parser::TokenParser; use crate::runtime::fault::Fault; use crate::runtime::run_thread::state::RunState; use crate::runtime::run_thread::ThreadInfo; @@ -14,14 +16,14 @@ use crate::runtime::run_thread::ThreadInfo; mod eval_res; /// Result type returned from the op parser. This is the Ok variant of a Result. -pub enum ParseOpRes { +pub enum ParseRes { /// Parsing successful. Parsed(T), /// Instruction not recognized, but there was no error. - Unknown(ArgParser), + Unknown(TokenParser), } -impl ParseOpRes { +impl ParseRes { /// Helper to construct an extension op pub fn ext(op: impl OpTrait) -> Self { Self::Parsed(Op::Ext(Box::new(op))) @@ -41,36 +43,35 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static { /// the argument list and either return Ok or Err. /// /// If the instruction keyword is not recognized, return Unknown with the unchanged argument list. - fn parse_op(&self, keyword: &str, arg_tokens: ArgParser) -> Result, CrsnError>; + fn parse_op(&self, keyword: &str, arg_tokens: TokenParser) -> Result, CrsnError>; + + /// Parse a generic S-expression (non-op) + fn parse_syntax(&self, keyword: &str, tokens: TokenParser, parsers: &[Box]) + -> Result>, CrsnError> + { + Ok(ParseRes::Unknown(tokens)) + } /// Drop an object referenced by a handle - fn drop_obj(&self, - #[allow(unused)] ti: &ThreadInfo, - #[allow(unused)] state: &mut RunState, - #[allow(unused)] handle: Value) -> Result, Fault> + fn drop_obj(&self, ti: &ThreadInfo, state: &mut RunState, handle: Value) + -> Result, Fault> { // Default impl - we do not support dropping this object Ok(None) } /// Run-time method called to read an object (using the object handle syntax) - fn read_obj(&self, - #[allow(unused)] ti: &ThreadInfo, - #[allow(unused)] state: &mut RunState, - #[allow(unused)] handle: Value, - #[allow(unused)] mask: Mask, - ) -> Result, Fault> { + fn read_obj(&self, ti: &ThreadInfo, state: &mut RunState, handle: Value, _mask: Mask) + -> Result, Fault> + { // Default impl - we do not support reading this object Ok(None) } /// Run-time method called to write an object (using the object handle syntax) - fn write_obj(&self, - #[allow(unused)] ti: &ThreadInfo, - #[allow(unused)] state: &mut RunState, - #[allow(unused)] handle: Value, - #[allow(unused)] mask: Mask, - ) -> Result, Fault> { + fn write_obj(&self, ti: &ThreadInfo, state: &mut RunState, handle: Value, mask: Mask) + -> Result, Fault> + { // Default impl - we do not support writing this object Ok(None) } diff --git a/crsn_arith/src/lib.rs b/crsn_arith/src/lib.rs index e3b2824..6f2eb58 100644 --- a/crsn_arith/src/lib.rs +++ b/crsn_arith/src/lib.rs @@ -1,7 +1,7 @@ use crsn::asm::error::CrsnError; use crsn::asm::instr::Op; -use crsn::asm::parse::arg_parser::ArgParser; -use crsn::module::{CrsnExtension, ParseOpRes}; +use crsn::asm::parse::arg_parser::TokenParser; +use crsn::module::{CrsnExtension, ParseRes}; mod defs; mod parse; @@ -21,7 +21,7 @@ impl CrsnExtension for ArithOps { "arith" } - fn parse_op(&self, keyword: &str, args: ArgParser) -> Result, CrsnError> { + fn parse_op(&self, keyword: &str, args: TokenParser) -> Result, CrsnError> { parse::parse(keyword, args) } } diff --git a/crsn_arith/src/parse.rs b/crsn_arith/src/parse.rs index 4912393..5aae72f 100644 --- a/crsn_arith/src/parse.rs +++ b/crsn_arith/src/parse.rs @@ -1,13 +1,13 @@ use crsn::asm::data::{Rd, Wr}; use crsn::asm::error::CrsnError; use crsn::asm::instr::Op; -use crsn::asm::parse::arg_parser::ArgParser; -use crsn::module::ParseOpRes; +use crsn::asm::parse::arg_parser::TokenParser; +use crsn::module::ParseRes; use crate::defs::ArithOp; -pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result, CrsnError> { - Ok(ParseOpRes::ext(match keyword { +pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result, CrsnError> { + Ok(ParseRes::ext(match keyword { "cmp" => { ArithOp::Compare { a: args.next_rd()?, @@ -430,7 +430,7 @@ pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result } _other => { - return Ok(ParseOpRes::Unknown(args)); + return Ok(ParseRes::Unknown(args)); } })) } diff --git a/crsn_screen/src/lib.rs b/crsn_screen/src/lib.rs index 39d07fc..af8d674 100644 --- a/crsn_screen/src/lib.rs +++ b/crsn_screen/src/lib.rs @@ -3,8 +3,8 @@ extern crate log; use crsn::asm::error::CrsnError; use crsn::asm::instr::Op; -use crsn::asm::parse::arg_parser::ArgParser; -use crsn::module::{CrsnExtension, ParseOpRes}; +use crsn::asm::parse::arg_parser::TokenParser; +use crsn::module::{CrsnExtension, ParseRes}; mod defs; mod parse; @@ -24,7 +24,7 @@ impl CrsnExtension for ScreenOps { "screen" } - fn parse_op(&self, keyword: &str, args: ArgParser) -> Result, CrsnError> { + fn parse_op(&self, keyword: &str, args: TokenParser) -> Result, CrsnError> { parse::parse(keyword, args) } } diff --git a/crsn_screen/src/parse.rs b/crsn_screen/src/parse.rs index 6b896fb..a6fcaa1 100644 --- a/crsn_screen/src/parse.rs +++ b/crsn_screen/src/parse.rs @@ -1,13 +1,13 @@ use crsn::asm::data::Rd; use crsn::asm::error::CrsnError; use crsn::asm::instr::Op; -use crsn::asm::parse::arg_parser::ArgParser; -use crsn::module::ParseOpRes; +use crsn::asm::parse::arg_parser::TokenParser; +use crsn::module::ParseRes; use crate::defs::ScreenOp; -pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result, CrsnError> { - Ok(ParseOpRes::ext(match keyword { +pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result, CrsnError> { + Ok(ParseRes::ext(match keyword { "sc-init" => { ScreenOp::ScreenInit { width: args.next_rd()?, @@ -41,7 +41,7 @@ pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result } _other => { - return Ok(ParseOpRes::Unknown(args)); + return Ok(ParseRes::Unknown(args)); } })) } diff --git a/crsn_stacks/src/lib.rs b/crsn_stacks/src/lib.rs index 828739f..da6a728 100644 --- a/crsn_stacks/src/lib.rs +++ b/crsn_stacks/src/lib.rs @@ -1,8 +1,8 @@ use crsn::asm::data::literal::Value; use crsn::asm::error::CrsnError; use crsn::asm::instr::Op; -use crsn::asm::parse::arg_parser::ArgParser; -use crsn::module::{CrsnExtension, ParseOpRes}; +use crsn::asm::parse::arg_parser::TokenParser; +use crsn::module::{CrsnExtension, ParseRes}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{RunState, ThreadInfo}; @@ -24,7 +24,7 @@ impl CrsnExtension for StackOps { "stacks" } - fn parse_op(&self, keyword: &str, args: ArgParser) -> Result, CrsnError> { + fn parse_op(&self, keyword: &str, args: TokenParser) -> Result, CrsnError> { parse::parse(keyword, args) } diff --git a/crsn_stacks/src/parse.rs b/crsn_stacks/src/parse.rs index b5368fc..f9192ff 100644 --- a/crsn_stacks/src/parse.rs +++ b/crsn_stacks/src/parse.rs @@ -1,12 +1,12 @@ use crsn::asm::error::CrsnError; use crsn::asm::instr::Op; -use crsn::asm::parse::arg_parser::ArgParser; -use crsn::module::ParseOpRes; +use crsn::asm::parse::arg_parser::TokenParser; +use crsn::module::ParseRes; use crate::defs::StackOp; -pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result, CrsnError> { - Ok(ParseOpRes::ext(match keyword { +pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result, CrsnError> { + Ok(ParseRes::ext(match keyword { "stack" => { StackOp::NewStack { dst: args.next_wr()?, @@ -28,7 +28,7 @@ pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result } _other => { - return Ok(ParseOpRes::Unknown(args)); + return Ok(ParseRes::Unknown(args)); } })) } diff --git a/examples/factorial.csn b/examples/factorial.csn index 11e17bc..e4ebf93 100644 --- a/examples/factorial.csn +++ b/examples/factorial.csn @@ -1,10 +1,10 @@ ( - (main + (proc main (call fac 5) (ld r0 res0) (halt) ) - (fac + (proc fac (cmp arg0 2 (eq? (ret 2))) (sub r0 arg0 1) diff --git a/examples/screen_bounce.csn b/examples/screen_bounce.csn index 068081c..cea432e 100644 --- a/examples/screen_bounce.csn +++ b/examples/screen_bounce.csn @@ -1,10 +1,10 @@ ; Set log level to "info" or above for the best results! ( - (main + (proc main (sc-init 800 600) - (sc-opt 1 0) ; auto blit - (sc-opt 2 60) ; frame rate + (sc-opt 1 1) ; auto blit + (sc-opt 2 25) ; frame rate (ld r0 5) ; x (ld r1 0) ; y @@ -15,16 +15,12 @@ (ld r5 0x3300ff) (:loop) - (add r5 0x000001) - (and r5 0xffffff) - (sc-px r0 r1 r5) - (add r0 r2) - (add r1 r3) - (cmp r0 799 (eq? (ld r2 -1))) - (cmp r0 0 (eq? (ld r2 1))) - (cmp r1 599 (eq? (ld r3 -1))) - (cmp r1 0 (eq? (ld r3 1))) - (sc-blit 0) + (add r5 0x000001) + (sc-px r0 r1 r5) + (add r0 r2) + (add r1 r3) + (cmp r0 799 (eq? (ld r2 -1)) (ne? (cmp r0 0 (eq? (ld r2 1))))) + (cmp r1 599 (eq? (ld r3 -1)) (ne? (cmp r1 0 (eq? (ld r3 1))))) (j :loop) ) )