Skip to content

Commit

Permalink
Merge code handling for procs and labels
Browse files Browse the repository at this point in the history
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à <[email protected]>
  • Loading branch information
mssola committed Dec 18, 2024
1 parent 5ddfd96 commit 9d9f102
Showing 1 changed file with 75 additions and 55 deletions.
130 changes: 75 additions & 55 deletions lib/xixanta/src/assembler.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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));
}
}
Expand All @@ -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 => {
Expand Down

0 comments on commit 9d9f102

Please sign in to comment.