diff options
| author | Miquel Sabaté Solà <mikisabate@gmail.com> | 2024-12-18 21:56:15 +0100 |
|---|---|---|
| committer | Miquel Sabaté Solà <mikisabate@gmail.com> | 2024-12-18 21:56:15 +0100 |
| commit | 9d9f10295059df278ed558142ce6c58c4f6a634e (patch) | |
| tree | a5496c4161e639a5993c36b11ebbc361420f475e /lib/xixanta | |
| parent | 5ddfd96f7311292e222a1bc8287eef33a8e097d8 (diff) | |
| download | tools.nes-9d9f10295059df278ed558142ce6c58c4f6a634e.tar.gz tools.nes-9d9f10295059df278ed558142ce6c58c4f6a634e.zip | |
Merge code handling for procs and labels
The handling of labels and proc's both when evaluating the context and
when bundling is almost identical. The only change is that proc's need
to change the context afterwards, but otherwise they need to create a
label just as regular ones.
Merge things as much as possible on both these cases, while also taking
the chance to do some much needed clean up around these areas, and
adding a bit of helpful comments in between.
Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'lib/xixanta')
| -rw-r--r-- | lib/xixanta/src/assembler.rs | 130 |
1 files changed, 75 insertions, 55 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs index 0341753..986f811 100644 --- a/lib/xixanta/src/assembler.rs +++ b/lib/xixanta/src/assembler.rs @@ -1,4 +1,4 @@ -use crate::errors::{Error, EvalError}; +use crate::errors::{ContextError, Error, EvalError}; use crate::mapping::Mapping; use crate::node::{ControlType, NodeType, PNode, PString}; use crate::object::{Bundle, Context, Object, ObjectType}; @@ -116,25 +116,38 @@ impl Assembler { self.fill() } + // Define a new variable by taking the given `id`. This variable will only + // be created if `id` is not empty. The function will error out if the given + // name is already taken. + fn define_variable(&mut self, id: &PString) -> Result<(), ContextError> { + if id.is_empty() { + return Ok(()); + } + + self.context.set_variable( + id, + &Object::new( + self.current_mapping, + self.current_segment, + ObjectType::Address, + ), + false, + ) + } + fn eval_context(&mut self, nodes: &[PNode]) -> Result<(), Vec<Error>> { let mut errors = Vec::new(); let mut current_macro = None; for (idx, node) in nodes.iter().enumerate() { match &node.node_type { + // At this stage we only define the label into the current + // context so it's known. The actual value cannot be computed + // right now as we don't know the segment size where it belongs + // yet. NodeType::Label => { - if !node.value.is_empty() { - if let Err(err) = self.context.set_variable( - &node.value, - &Object::new( - self.current_mapping, - self.current_segment, - ObjectType::Address, - ), - false, - ) { - errors.push(Error::Context(err)); - } + if let Err(err) = self.define_variable(&node.value) { + errors.push(Error::Context(err)); } } NodeType::Assignment => { @@ -205,17 +218,10 @@ impl Assembler { } current_macro = None; } + // Same as NodeType::Label. ControlType::StartProc => { let proc_name = &node.left.as_ref().unwrap().value; - if let Err(err) = self.context.set_variable( - proc_name, - &Object::new( - self.current_mapping, - self.current_segment, - ObjectType::Address, - ), - false, - ) { + if let Err(err) = self.define_variable(proc_name) { errors.push(Error::Context(err)); } } @@ -237,45 +243,59 @@ impl Assembler { } } + // Apply the current segment offset to the label identified by `id` unless + // it's empty (i.e. anonymous label). In either case, the computed label + // will be pushed into the context's list of known labels with the current + // segment offset. + fn apply_segment_offset_to_label(&mut self, id: &PString) -> Result<(), ContextError> { + let segment = &self.mappings[self.current_mapping].segments[self.current_segment]; + let value = segment.offset.to_le_bytes(); + let object = Object { + bundle: Bundle { + bytes: [value[0], value[1], value[2]], + size: 2, + address: 0, + cycles: 0, + affected_on_page: false, + resolved: false, + }, + mapping: self.current_mapping, + segment: self.current_segment, + object_type: ObjectType::Address, + }; + + if !id.is_empty() { + self.context.set_variable(id, &object, true)?; + } + self.context.add_label(&object); + + Ok(()) + } + fn bundle(&mut self, nodes: &Vec<PNode>) -> Result<(), Vec<Error>> { let mut errors = Vec::new(); for node in nodes { match node.node_type { - NodeType::Label | NodeType::Control(ControlType::StartProc) => { - let segment = - &self.mappings[self.current_mapping].segments[self.current_segment]; - let value = segment.offset.to_le_bytes(); - let object = Object { - bundle: Bundle { - bytes: [value[0], value[1], value[2]], - size: 2, - address: 0, - cycles: 0, - affected_on_page: false, - resolved: false, - }, - mapping: self.current_mapping, - segment: self.current_segment, - object_type: ObjectType::Address, - }; - - if matches!(node.node_type, NodeType::Control(ControlType::StartProc)) { - let proc_name = &node.left.as_ref().unwrap().value; - if let Err(err) = self.context.set_variable(proc_name, &object, true) { - errors.push(Error::Context(err)); - } - } else { - if !node.value.is_empty() { - if let Err(err) = self.context.set_variable(&node.value, &object, true) - { - errors.push(Error::Context(err)); - } - } + // Initialize the label to the offset address of the current + // segment. Note that this is only the offset from the beginning + // of the offset, the effective address will only be available + // after calling `Context::get_variable` + NodeType::Label => { + if let Err(e) = self.apply_segment_offset_to_label(&node.value) { + errors.push(Error::Context(e)); + } + } + // Same as with labels but with the addition that ".proc" + // introduces a new context. Hence, first act as a label, and + // then open up its inner context. + NodeType::Control(ControlType::StartProc) => { + let proc_name = &node.left.as_ref().unwrap().value; + if let Err(e) = self.apply_segment_offset_to_label(proc_name) { + errors.push(Error::Context(e)); } - self.context.add_label(&object); - if matches!(node.node_type, NodeType::Control(ControlType::StartProc)) { - let _ = self.context.change_context(node); + if let Err(e) = self.context.change_context(node) { + errors.push(Error::Context(e)); } } NodeType::Instruction => { |
