Initial Commit Test 1

This commit is contained in:
2025-11-30 03:36:50 -06:00
commit a858f0d0a4
6 changed files with 2309 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

103
Cargo.lock generated Normal file
View File

@@ -0,0 +1,103 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ariadne"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "367fd0ad87307588d087544707bc5fbf4805ded96c7db922b70d368fa1cb5702"
dependencies = [
"unicode-width",
"yansi",
]
[[package]]
name = "bitfield"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac"
[[package]]
name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
dependencies = [
"proc-macro2",
]
[[package]]
name = "peg"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477"
dependencies = [
"peg-macros",
"peg-runtime",
]
[[package]]
name = "peg-macros"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71"
dependencies = [
"peg-runtime",
"proc-macro2",
"quote",
]
[[package]]
name = "peg-runtime"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ulp_macro"
version = "0.1.0"
dependencies = [
"ariadne",
"bitfield",
"litrs",
"peg",
"proc-macro2",
"quote",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"

17
Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "ulp_macro"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
quote = "1.0"
bitfield = "0.14.0"
proc-macro2 = "1.0"
litrs = "0.4.0"
peg = "0.8.1"
[dev-dependencies]
ariadne = "0.2.0"

1504
src/codegen.rs Normal file

File diff suppressed because it is too large Load Diff

101
src/lib.rs Normal file
View File

@@ -0,0 +1,101 @@
#![feature(proc_macro_span)]
use litrs::StringLit;
use proc_macro::{self, TokenStream};
use quote::format_ident;
use quote::quote;
mod parser;
use parser::parse;
use quote::quote_spanned;
mod codegen;
use crate::codegen::create_codegen;
use crate::codegen::CodeGen;
#[proc_macro]
pub fn ulp_asm(input: TokenStream) -> TokenStream {
let first_token = input.into_iter().next().expect("no input");
let arg = StringLit::try_from(&first_token).expect("Expecting a string argument");
let src = arg.value();
let ast = parse(src);
match ast {
Ok(ast) => {
let code = create_codegen().generate(ast);
match code {
Ok((code, labels)) => {
let code_len = code.len();
let mut accessors = Vec::new();
for lbl in labels {
let getter_name = format_ident!("get_{}", lbl.name);
let setter_name = format_ident!("set_{}", lbl.name);
let address = lbl.address;
accessors.push(quote! {
fn #getter_name(&self) -> u16 {
unsafe {(((#address + 0x5000_0000) as *const u32).read_volatile() & 0xffff) as u16}
}
fn #setter_name(&self, value: u16) {
unsafe {((#address + 0x5000_0000) as *mut u32).write_volatile(value as u32)}
}
});
}
let tokens = quote! {
{
struct _Ulp {
code: [u8; #code_len],
}
impl _Ulp {
#(#accessors)*
fn load(&self) {
unsafe {
core::ptr::copy_nonoverlapping(
self.code.as_ptr() as *const u8,
0x5000_0000 as *mut u8,
#code_len as usize
);
}
}
}
_Ulp {
code: [ #(#code),* ]
}
}
};
tokens.into()
}
Err(error) => {
let error_msg = format!("{:?}", error);
let span = first_token.span().into();
let tokens = quote_spanned! {span=> compile_error!(#error_msg)};
tokens.into()
}
}
}
Err(error) => {
let error_msg = error.to_string();
let span = match first_token {
proc_macro::TokenTree::Group(_) => todo!(),
proc_macro::TokenTree::Ident(_) => todo!(),
proc_macro::TokenTree::Punct(_) => todo!(),
proc_macro::TokenTree::Literal(ref lit) => lit
.subspan(error.location.offset..(error.location.offset + 1))
.unwrap_or_else(|| first_token.span()),
}
.into();
let tokens = quote_spanned! {span=> compile_error!(#error_msg)};
tokens.into()
}
}
}

583
src/parser.rs Normal file
View File

@@ -0,0 +1,583 @@
#[cfg(test)]
use ariadne::{Label, Report, ReportKind, Source};
use peg::str::LineCol;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum Reg {
R0 = 0,
R1 = 1,
R2 = 2,
R3 = 3,
}
#[derive(Debug, Clone)]
pub(crate) enum Value {
Number(u32),
Identifier(String),
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub(crate) enum Condition {
Eq,
Lt,
Le,
Gt,
Ge,
Ov,
}
#[derive(Debug, Clone)]
pub(crate) enum Instruction {
Move(Reg, Reg),
MoveImmediate(Reg, Value),
Store(Reg, Reg, Value),
Load(Reg, Reg, Value),
Add(Reg, Reg, Reg),
AddImmediate(Reg, Reg, Value),
Nop,
Sub(Reg, Reg, Reg),
SubImmediate(Reg, Reg, Value),
And(Reg, Reg, Reg),
AndImmediate(Reg, Reg, Value),
Or(Reg, Reg, Reg),
OrImmediate(Reg, Reg, Value),
Lsh(Reg, Reg, Reg),
LshImmediate(Reg, Reg, Value),
Rsh(Reg, Reg, Reg),
RshImmediate(Reg, Reg, Value),
Jump(Reg),
JumpImmediate(Value),
JumpConditional(Reg, Condition),
JumpConditionalImmediate(Value, Condition),
JumpR(Value, Value, Condition),
JumpS(Value, Value, Condition),
StageRst,
StageInc(Value),
StageDec(Value),
Halt,
Wake,
Sleep(Value),
Wait(Value),
Tsens(Reg, Value),
Adc(Reg, Value, Value),
I2cRd(Value, Value, Value, Value),
I2cWr(Value, Value, Value, Value, Value),
RegRd(Value, Value, Value),
RegWr(Value, Value, Value, Value),
Comment,
Label(String),
Set(String, Value),
Long(Value),
Global(String),
}
peg::parser! {
grammar pparser() for str {
rule i(literal: &'static str)
= input:$([_]*<{literal.len()}>)
{? if input.eq_ignore_ascii_case(literal) { Ok(()) } else { Err(literal) } }
rule keyword_chars() = ['A'..='Z'|'0'..='9']
rule ws() = [' '|'\r'|'\n']
rule space() = [' '| '\t']
rule newline() = ['\r'|'\n']
rule eof() = ![_]
rule any() = [' '] / ['\t'] / ['a'..='z'] / ['A'..='Z'] / ['0'..='9'] / ['.'] / ['ö'] / ['ä'] / [',']
/ ['ü'] / ['Ö'] / ['Ä'] / ['Ü'] / ['-'] / ['_'] / ['('] / [')'] / [','] / ['['] / [']'] / ['+'] / ['=']
/ ['&'] / ['|'] / ['<'] / ['>']
rule identifier_char() = ['a'..='z'] / ['A'..='Z'] / ['0'..='9'] / ['_']
rule register() -> Reg
= s:$(i("R0") / i("R1") / i("R2") / i("R3")) {
match s.to_uppercase().as_str() {
"R0" => Reg::R0,
"R1" => Reg::R1,
"R2" => Reg::R2,
"R3" => Reg::R3,
_ => panic!()
}
}
rule condition() -> Condition
= s:$(i("EQ") / i("LT") / i("LE") / i("GT") / i("GE") / i("OV")) {
match s.to_uppercase().as_str() {
"EQ" => Condition::Eq,
"LT" => Condition::Lt,
"LE" => Condition::Le,
"GT" => Condition::Gt,
"GE" => Condition::Ge,
"OV" => Condition::Ov,
_ => panic!()
}
}
rule number_hex() -> Value = "0x" s:$(['0'..='9'|'a'..='f'|'A'..='F']+) {
Value::Number(u32::from_str_radix(s, 16).unwrap())
}
rule number_bin() -> Value = "0b" s:$(['0'..='1']+) {
Value::Number(u32::from_str_radix(s, 2).unwrap())
}
rule number() -> Value = s:$(['0'..='9']+) {
Value::Number(u32::from_str_radix(s, 10).unwrap())
}
rule identifier() -> Value
= s:$(identifier_char()*) { Value::Identifier(s.to_string()) }
rule number_or_symbol() -> Value
= v:(number_hex() / number_bin() / number() / identifier()) {
v
}
rule move_instr() -> Instruction
= i("MOVE")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())* {
Instruction::Move(r0, r1)
}
rule move_immediate_instr() -> Instruction
= i("MOVE")(space())+r0:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::MoveImmediate(r0, l)
}
rule store_instr() -> Instruction
= i("ST")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::Store(r0, r1, l)
}
rule load_instr() -> Instruction
= i("LD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::Load(r0, r1, l)
}
rule add_immediate_instr() -> Instruction
= i("ADD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::AddImmediate(r0, r1, l)
}
rule add_instr() -> Instruction
= i("ADD")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
Instruction::Add(r0, r1, r3)
}
rule nop_instr() -> Instruction
= i("NOP")(space())* {
Instruction::Nop
}
rule sub_immediate_instr() -> Instruction
= i("SUB")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::SubImmediate(r0, r1, l)
}
rule sub_instr() -> Instruction
= i("SUB")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
Instruction::Sub(r0, r1, r3)
}
rule and_immediate_instr() -> Instruction
= i("AND")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::AndImmediate(r0, r1, l)
}
rule and_instr() -> Instruction
= i("AND")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
Instruction::And(r0, r1, r3)
}
rule or_immediate_instr() -> Instruction
= i("OR")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::OrImmediate(r0, r1, l)
}
rule or_instr() -> Instruction
= i("OR")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
Instruction::Or(r0, r1, r3)
}
rule lsh_immediate_instr() -> Instruction
= i("LSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::LshImmediate(r0, r1, l)
}
rule lsh_instr() -> Instruction
= i("LSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
Instruction::Lsh(r0, r1, r3)
}
rule rsh_immediate_instr() -> Instruction
= i("RSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::RshImmediate(r0, r1, l)
}
rule rsh_instr() -> Instruction
= i("RSH")(space())+r0:(register())(space())*[','](space())*r1:(register())(space())*[','](space())*r3:(register())(space())* {
Instruction::Rsh(r0, r1, r3)
}
rule jump_instr() -> Instruction
= i("JUMP")(space())+r0:(register())(space())* {
Instruction::Jump(r0)
}
rule jump_immediate_instr() -> Instruction
= i("JUMP")(space())+v:(number_or_symbol())(space())* {
Instruction::JumpImmediate(v)
}
rule jump_conditional_instr() -> Instruction
= i("JUMP")(space())+r0:(register())(space())*[','](space())*c:(condition())(space())* {
Instruction::JumpConditional(r0, c)
}
rule jump_conditional_immediate_instr() -> Instruction
= i("JUMP")(space())+v:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* {
Instruction::JumpConditionalImmediate(v, c)
}
rule jumpr_conditional_instr() -> Instruction
= i("JUMPR")(space())+step:(number_or_symbol())(space())*[','](space())thr:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* {
Instruction::JumpR(step, thr, c)
}
rule jumps_conditional_instr() -> Instruction
= i("JUMPS")(space())+step:(number_or_symbol())(space())*[','](space())thr:(number_or_symbol())(space())*[','](space())*c:(condition())(space())* {
Instruction::JumpS(step, thr, c)
}
rule stage_rst_instr() -> Instruction
= i("STAGE_RST")(space())* {
Instruction::StageRst
}
rule stage_inc_instr() -> Instruction
= i("STAGE_INC")(space())+value:(number_or_symbol())(space())* {
Instruction::StageInc(value)
}
rule stage_dec_instr() -> Instruction
= i("STAGE_DEC")(space())+value:(number_or_symbol())(space())* {
Instruction::StageDec(value)
}
rule halt_instr() -> Instruction
= i("HALT")(space())* {
Instruction::Halt
}
rule wake_instr() -> Instruction
= i("WAKE")(space())* {
Instruction::Wake
}
rule sleep_instr() -> Instruction
= i("SLEEP")(space())+v:(number_or_symbol())(space())* {
Instruction::Sleep(v)
}
rule wait_instr() -> Instruction
= i("WAIT")(space())+v:(number_or_symbol())(space())* {
Instruction::Wait(v)
}
rule tsens_instr() -> Instruction
= i("TSENS")(space())+r0:(register())(space())*[','](space())*l:(number_or_symbol())(space())* {
Instruction::Tsens(r0, l)
}
rule adc_instr() -> Instruction
= i("ADC")(space())+r0:(register())(space())*[','](space())*l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())* {
Instruction::Adc(r0, l0, l1)
}
rule i2c_rd_instr() -> Instruction
= i("I2C_RD")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())*[','](space())*l3:(number_or_symbol())(space())* {
Instruction::I2cRd(l0, l1, l2, l3)
}
rule i2c_wr_instr() -> Instruction
= i("I2C_WR")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())*[','](space())*l3:(number_or_symbol())(space())*[','](space())*l4:(number_or_symbol())(space())* {
Instruction::I2cWr(l0, l1, l2, l3, l4)
}
rule reg_rd_instr() -> Instruction
= i("REG_RD")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())* {
Instruction::RegRd(l0, l1, l2)
}
rule reg_wr_instr() -> Instruction
= i("REG_WR")(space())+l0:(number_or_symbol())(space())*[','](space())*l1:(number_or_symbol())(space())*[','](space())*l2:(number_or_symbol())(space())*[','](space())*l3:(number_or_symbol())(space()*) {
Instruction::RegWr(l0, l1, l2, l3)
}
rule comment() -> Instruction = "//" any()* { Instruction::Comment }
rule block_comment() -> Instruction = "/*" (any() / newline())* "*/" { Instruction::Comment }
rule label() -> Instruction
= s:$(identifier_char()*) ":"(space())* {
Instruction::Label(s[..s.len()].to_string())
}
rule set_pseudo_instr() -> Instruction
= i(".set")(space())+label:(identifier())(space())*[','](space())*l:(number_or_symbol())(space())* {
if let Value::Identifier(ident) = label { Instruction::Set(ident, l) } else { panic!() }
}
rule long_pseudo_instr() -> Instruction
= i(".long")(space())+l:(number_or_symbol())(space())* {
Instruction::Long(l)
}
rule global_pseudo_instr() -> Instruction
= i(".global")(space())+label:(identifier())(space())* {
if let Value::Identifier(ident) = label { Instruction::Global(ident) } else { panic!() }
}
rule instr() -> Instruction
= ((space() / newline())*) c:(
move_instr() /
move_immediate_instr() /
store_instr() /
load_instr() /
add_instr() /
add_immediate_instr() /
nop_instr() /
sub_instr() /
sub_immediate_instr() /
and_instr() /
and_immediate_instr() /
or_instr() /
or_immediate_instr() /
lsh_instr() /
lsh_immediate_instr() /
rsh_instr() /
rsh_immediate_instr() /
jump_conditional_instr() /
jump_conditional_immediate_instr() /
jump_instr() /
jump_immediate_instr() /
jumpr_conditional_instr() /
jumps_conditional_instr() /
stage_rst_instr() /
stage_inc_instr() /
stage_dec_instr() /
halt_instr() /
wake_instr() /
sleep_instr() /
wait_instr() /
tsens_instr() /
adc_instr() /
i2c_rd_instr() /
i2c_wr_instr() /
reg_rd_instr() /
reg_wr_instr() /
comment() /
block_comment() /
label() /
set_pseudo_instr() /
long_pseudo_instr() /
global_pseudo_instr()
) ((space() / newline())*) { c }
pub(crate) rule parse() -> Vec<Instruction>
= ((newline()*) (ws()*)) c:(instr()) ** (newline()*) {
c
}
}
}
pub(crate) fn parse(src: &str) -> Result<Vec<Instruction>, peg::error::ParseError<LineCol>> {
pparser::parse(src)
}
#[cfg(test)]
pub(crate) fn print_error(src: &str, error: &peg::error::ParseError<LineCol>) {
Report::build(ReportKind::Error, (), 34)
.with_message(error.to_string())
.with_label(
Label::new(error.location.offset..(error.location.offset + 1))
.with_message("Error here"),
)
.finish()
.print(Source::from(src))
.unwrap();
}
#[cfg(test)]
fn parse_and_print_error(src: &str) -> Result<Vec<Instruction>, peg::error::ParseError<LineCol>> {
let res = parse(src);
match &res {
Ok(_) => (),
Err(err) => print_error(src, &err),
}
println!("{:?}", res);
res
}
#[test]
fn test_simple() {
let res = parse_and_print_error("\r\n MOVE R0, R1\r\n ADD R3, R3, 1 //Zeiger auf nächsten 32-Bit-Speicherplatz\n MOVE R1,R2 ");
assert!(res.is_ok())
}
#[test]
fn test_simple2() {
let res = parse_and_print_error(
"ST R0, R3, 0 //2.Element über Offset adressieren\nADD R3, R3, 1 ",
);
assert!(res.is_ok())
}
#[test]
fn test_simple2_identifier() {
let res =
parse("ST R0, R3, myvalue //2.Element über Offset adressieren\nADD R3, R3, VALUE ");
assert!(res.is_ok())
}
#[test]
fn test_numbers() {
let res = parse_and_print_error(
"ST R0, R3, 0 \n ST R0, R3, 0xabcd \n ST R0, R3, 0b1010111011 \n \n",
);
assert!(res.is_ok())
}
#[test]
fn test_comments() {
let res = parse_and_print_error("move R0, R1 // test\n\nMove R2,R3\n");
assert!(res.is_ok())
}
#[test]
fn test2() {
let res = parse_and_print_error(
"
label:
MOVE R3, 555 // Some Comment
MOVE R0, 1234
ST R0, R3, 0
MOVE R0, 5678
ST R0, R3, 4 //2.Element über Offset adressieren
//oder
ADD R3, R3, 1 //Zeiger auf nächsten 32-Bit-Speicherplatz
ST R0, R3, 0 //2.Element mit Nulloffset adressieren
ST R0, R3, 0 //2.Element mit Nulloffset adressieren
JUMP label
",
);
assert!(res.is_ok())
}
#[test]
fn parse_complex_thing() {
let res = parse_and_print_error(
"is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit
AND r0, r0, 1
JUMP is_rdy_for_wakeup, eq // Retry until the bit is set
WAKE // Trigger wake up
REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN)
HALT // Stop the ULP program
// After these instructions, SoC will wake up,
// and ULP will not run again until started by the main program.",
);
assert!(res.is_ok())
}
#[test]
fn parse_others() {
let res = parse_and_print_error(
"is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit
.set foo, 0x234
.set foo, 234
.set foo2, foo
.global my_label
// After these instructions, SoC will wake up,
// and ULP will not run again until started by the main program.
JUMPR label, 1, GE
JUMPS label, 1, GE
some_more_stuff:
move r1, r0
jump p1_status_changed, eq
move r3, p1_status
rsh r0, r1, 7
and r0, r0, 1
st r0, r3, 0
move r3, p2_status
rsh r0, r1, 8
and r0, r0, 1
st r0, r3, 0
move r3, p3_status
rsh r0, r1, 9
and r0, r0, 1
st r0, r3, 0
// check if p1 status changed
rsh r0, r1, 7
and r0, r0, 1
move r3, p1_status_next
ld r3, r3, 0
add r3, r0, r3
and r3, r3, 1
jump p1_status_changed, eq
// check if p2 status changed
rsh r0, r1, 8
and r0, r0, 1
move r3, p2_status_next
ld r3, r3, 0
add r3, r0, r3
and r3, r3, 1
jump p2_status_changed, eq
/* check if p3 status changed */
rsh r0, r1, 9
and r0, r0, 1
move r3, p3_status_next
ld r3, r3, 0
add r3, r0, r3
and r3, r3, 1
jump p3_status_changed, eq
/*
multi line comments
*/
halt
ADC R1, 0, 1
TSENS R1, 1000
REG_RD 0x120, 7, 4
REG_WR 0x120, 7, 0, 0x10
I2C_WR 0x20, 0x33, 7, 0, 1
I2C_RD 0x10, 7, 0, 0
WAIT 10
SLEEP 1
label:
.long 0
",
);
assert!(res.is_ok())
}