From 4c3d4f15f24e32d043670939ace34fdb6993a51b Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Wed, 18 Dec 2024 16:21:49 +0100 Subject: Add support for jumping into .proc's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now defining a proc only involved pushing/popping the context. Here we also allow it to create a label so it can be referenced by instructions like jsr. Signed-off-by: Miquel Sabaté Solà --- lib/xixanta/src/assembler.rs | 72 ++++++++++++++++++++++++++++++++++++++++---- lib/xixanta/src/object.rs | 2 +- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs index 7b5fb65..f42db16 100644 --- a/lib/xixanta/src/assembler.rs +++ b/lib/xixanta/src/assembler.rs @@ -205,6 +205,20 @@ impl Assembler { } current_macro = None; } + 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, + ) { + errors.push(Error::Context(err)); + } + } _ => {} } if let Err(err) = self.context.change_context(node) { @@ -228,7 +242,7 @@ impl Assembler { for node in nodes { match node.node_type { - NodeType::Label => { + NodeType::Label | NodeType::Control(ControlType::StartProc) => { let segment = &self.mappings[self.current_mapping].segments[self.current_segment]; let value = segment.offset.to_le_bytes(); @@ -246,12 +260,23 @@ impl Assembler { object_type: ObjectType::Address, }; - if !node.value.is_empty() { - if let Err(err) = self.context.set_variable(&node.value, &object, true) { + 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)); + } + } } self.context.add_label(&object); + if matches!(node.node_type, NodeType::Control(ControlType::StartProc)) { + let _ = self.context.change_context(node); + } } NodeType::Instruction => { if self.can_bundle { @@ -1523,7 +1548,7 @@ Variable = 1 Yet = 3 Yet = 4 "#, - "'Yet' already defined in the global scope: you cannot re-assign variables", + "'Yet' already defined in the global scope: you cannot re-assign names", 8, ); } @@ -2036,7 +2061,42 @@ nop assert_eq!(res[2].bytes[1], 0x00); } - // TODO: function calls + #[test] + fn jsr_to_proc() { + let mut asm = Assembler::new(EMPTY.to_vec()); + asm.mappings[0].segments[0].bundles = minimal_header(); + asm.mappings[0].offset = 6; + asm.current_mapping = 1; + let res = &asm + .assemble( + r#"nop +.proc hello + nop + rts +.endproc + jsr hello +"# + .as_bytes(), + ) + .unwrap()[0x10..]; + + assert_eq!(res.len(), 4); + + // First two nop's + rts + assert_eq!(res[0].size, 1); + assert_eq!(res[0].bytes[0], 0xEA); + assert_eq!(res[1].size, 1); + assert_eq!(res[1].bytes[0], 0xEA); + assert_eq!(res[2].size, 1); + assert_eq!(res[2].bytes[0], 0x60); + + // jsr hello + assert_eq!(res[3].size, 3); + assert_eq!(res[3].bytes[0], 0x20); + assert_eq!(res[3].bytes[1], 0x01); + assert_eq!(res[3].bytes[2], 0x80); + } + // TODO: labels and jumps inside of proc's, macros, etc. // Control statements @@ -2283,7 +2343,7 @@ MACRO(1) assert_eq!( res.first().unwrap().to_string(), "Evaluation error (line 5): 'Var' already defined in the global scope: \ - you cannot re-assign variables." + you cannot re-assign names." ); } diff --git a/lib/xixanta/src/object.rs b/lib/xixanta/src/object.rs index 3d0c9e7..8f177a1 100644 --- a/lib/xixanta/src/object.rs +++ b/lib/xixanta/src/object.rs @@ -230,7 +230,7 @@ impl Context { if !overwrite { return Err(ContextError { message: format!( - "'{}' already defined in {}: you cannot re-assign variables", + "'{}' already defined in {}: you cannot re-assign names", id.value, self.to_human() ), -- cgit v1.2.3