aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xixanta/src')
-rw-r--r--lib/xixanta/src/assembler.rs79
-rw-r--r--lib/xixanta/src/node.rs2
-rw-r--r--lib/xixanta/src/opcodes.rs1
3 files changed, 72 insertions, 10 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index a58c45e..32d7c14 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -1244,7 +1244,7 @@ impl<'a> Assembler<'a> {
}
}
- fn evaluate_control_statement(&mut self, node: &PNode) -> Result<(), Error> {
+ fn evaluate_control_statement(&mut self, node: &'a PNode) -> Result<(), Error> {
// This might just be a statement that changes the context (e.g.
// ".macro", ".proc", etc.). In this case change the context and leave
// early.
@@ -1272,6 +1272,7 @@ impl<'a> Assembler<'a> {
self.push_evaluated_arguments(node, 2)
}
NodeType::Control(ControlType::ReserveMemory) => self.reserve_memory(node),
+ NodeType::Control(ControlType::Asciiz) => self.push_ascii_string(node),
NodeType::Control(ControlType::Segment) => self.switch_to_segment(node),
NodeType::Control(ControlType::IncBin) => {
self.incbin(node.args.as_ref().unwrap().first().unwrap())
@@ -1622,18 +1623,26 @@ impl<'a> Assembler<'a> {
Ok(())
}
- fn switch_to_segment(&mut self, node: &PNode) -> Result<(), Error> {
- // First of all, fetch the argument for the ".segment" statement and
- // validate that it has some basic format. Note that the existence of
- // exactly one argument is guaranteed by the parser and, thus,
- // `unwrap()` calls are not dangerous in this context.
+ // Fetch the first argument from `node`, validate that it's a quoted string,
+ // and return its inner value. Note that before calling this function there
+ // must be the guarantee that there's at least one argument inside of the
+ // node.
+ fn fetch_quoted_first_argument(&mut self, node: &'a PNode) -> Result<&'a str, Error> {
let arg = &node.args.as_ref().unwrap().first().unwrap();
let val = &arg.value.value;
- if val.len() < 3 || !val.starts_with('"') || !val.ends_with('"') {
+ if val.len() < 3 {
+ return Err(Error {
+ line: node.value.line,
+ message: "unpermitted empty argument".to_string(),
+ source: self.source_for(node),
+ global: false,
+ });
+ }
+ if !val.starts_with('"') || !val.ends_with('"') {
return Err(Error {
line: node.value.line,
message: format!(
- "segment declaration has to be written inside of double quotes ('{}' given instead)",
+ "declaration has to be written inside of double quotes ('{}' given instead)",
val,
),
source: self.source_for(node),
@@ -1641,8 +1650,34 @@ impl<'a> Assembler<'a> {
});
}
- // Validate the segment name.
- let name = &val[1..val.len() - 1];
+ Ok(&val[1..val.len() - 1])
+ }
+
+ // Push fill bundles which correspond to the string referenced in `node`. An
+ // 0x00 bundle will also be pushed at the end.
+ fn push_ascii_string(&mut self, node: &'a PNode) -> Result<(), Error> {
+ let string = self.fetch_quoted_first_argument(node)?;
+
+ for ch in string.chars() {
+ if !ch.is_ascii() {
+ return Err(Error {
+ line: node.value.line,
+ message: "string can only contain ASCII characters".to_string(),
+ source: self.source_for(node),
+ global: false,
+ });
+ }
+
+ self.push_bundle(Bundle::fill(ch as u8), node)?;
+ }
+ self.push_bundle(Bundle::fill(0x00), node)?;
+
+ Ok(())
+ }
+
+ // Change the current segment to the one referenced in `node`.
+ fn switch_to_segment(&mut self, node: &'a PNode) -> Result<(), Error> {
+ let name = self.fetch_quoted_first_argument(node)?;
if name
.chars()
.any(|ch| !(ch.is_ascii_alphanumeric() || ch == '_' || ch == '-'))
@@ -3096,6 +3131,30 @@ jsr Movement::update
}
#[test]
+ fn asciiz() {
+ let res = just_bundles(".asciiz \"hello\"");
+
+ assert_eq!(res.len(), 6);
+ assert_eq!(res[0].value(), 0x68);
+ assert_eq!(res[1].value(), 0x65);
+ assert_eq!(res[2].value(), 0x6C);
+ assert_eq!(res[3].value(), 0x6C);
+ assert_eq!(res[4].value(), 0x6F);
+ assert_eq!(res[5].value(), 0x00);
+ }
+
+ #[test]
+ fn bad_asciiz() {
+ let res = just_assemble(".asciiz \"\"");
+
+ assert_eq!(res.errors.len(), 1);
+ assert_eq!(
+ res.errors[0].to_string(),
+ "unpermitted empty argument (line 4)"
+ );
+ }
+
+ #[test]
fn hi_lo_byte() {
let res = just_bundles(
r#"
diff --git a/lib/xixanta/src/node.rs b/lib/xixanta/src/node.rs
index 8fc3c73..04655cb 100644
--- a/lib/xixanta/src/node.rs
+++ b/lib/xixanta/src/node.rs
@@ -142,6 +142,7 @@ pub enum ControlType {
EndRepeat,
IncludeSource,
ReserveMemory,
+ Asciiz,
}
impl fmt::Display for ControlType {
@@ -164,6 +165,7 @@ impl fmt::Display for ControlType {
ControlType::EndRepeat => write!(f, ".endrepeat"),
ControlType::IncludeSource => write!(f, ".include"),
ControlType::ReserveMemory => write!(f, ".res"),
+ ControlType::Asciiz => write!(f, ".asciiz"),
}
}
}
diff --git a/lib/xixanta/src/opcodes.rs b/lib/xixanta/src/opcodes.rs
index 455a8ea..7f9da45 100644
--- a/lib/xixanta/src/opcodes.rs
+++ b/lib/xixanta/src/opcodes.rs
@@ -750,6 +750,7 @@ lazy_static! {
functions.insert(String::from(".endrepeat"), Control { control_type: ControlType::EndRepeat, has_identifier: None, required_args: None, touches_context: true });
functions.insert(String::from(".include"), Control { control_type: ControlType::IncludeSource, has_identifier: None, required_args: Some((1, 1)), touches_context: false });
functions.insert(String::from(".res"), Control { control_type: ControlType::ReserveMemory, has_identifier: None, required_args: Some((1, 2)), touches_context: false });
+ functions.insert(String::from(".asciiz"), Control { control_type: ControlType::Asciiz, has_identifier: None, required_args: Some((1, 1)), touches_context: false });
functions
};