diff --git a/asm-lsp/parser.rs b/asm-lsp/parser.rs index 92d2ca6a..028e8db9 100644 --- a/asm-lsp/parser.rs +++ b/asm-lsp/parser.rs @@ -1,4 +1,4 @@ -use crate::ustr; +use crate::{ustr, AvrStatusRegister, AvrTiming}; use std::collections::HashMap; use std::env::args; use std::fs; @@ -1305,6 +1305,450 @@ pub fn populate_name_to_instruction_map( } } +/// Parse the provided XML contents and return a vector of all the instructions based on that. +/// If parsing fails, the appropriate error will be returned instead. +/// +/// Current function assumes that the XML file is already read and that it's been given a reference +/// to its contents (`&str`). +/// +/// # Errors +/// +/// This function is highly specialized to parse a handful of files and will panic or return +/// `Err` for most mal-formed inputs +/// +/// # Panics +/// +/// This function is highly specialized to parse a handful of files and will panic or return +/// `Err` for most mal-formed/unexpected inputs +pub fn populate_avr_instructions(xml_contents: &str) -> Result> { + // initialise the instruction set + let mut instructions_map = HashMap::::new(); + + // iterate through the XML + let mut reader = Reader::from_str(xml_contents); + + // ref to the instruction that's currently under construction + let mut curr_instruction = Instruction::default(); + let mut curr_instruction_form = InstructionForm::default(); + let mut arch: Arch = Arch::None; + let mut curr_version: Option = None; + + debug!("Parsing avr instruction XML contents..."); + loop { + match reader.read_event() { + // start event + Ok(Event::Start(ref e)) => { + match e.name() { + QName(b"InstructionSet") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if b"name" == key.into_inner() { + arch = Arch::from_str(ustr::get_str(&value)).unwrap_or_else(|e| { + panic!("Failed parse Arch {} -- {e}", ustr::get_str(&value)) + }); + assert!(arch == Arch::Avr); + } else { + warn!("Failed to parse architecture name"); + } + } + } + QName(b"Instruction") => { + // start of a new instruction + curr_instruction = Instruction::default(); + curr_instruction.arch = arch; + + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + match ustr::get_str(key.into_inner()) { + "name" => { + let name = ustr::get_str(&value); + curr_instruction.name = name.to_ascii_lowercase(); + } + "summary" => { + ustr::get_str(&value).clone_into(&mut curr_instruction.summary); + } + _ => {} + } + } + } + // Versions are defined a per-instruction form basis + QName(b"Version") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if "value" == ustr::get_str(key.into_inner()) { + curr_version = Some(ustr::get_str(&value).to_string()); + } + } + } + QName(b"InstructionForm") => { + assert!(curr_version.is_some()); + // new instruction form + curr_instruction_form = InstructionForm::default(); + curr_instruction_form.avr_version.clone_from(&curr_version); + + // iterate over the attributes + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + match ustr::get_str(key.into_inner()) { + "mnemonic" => { + curr_instruction_form.avr_mneumonic = + Some(ustr::get_str(&value).to_owned()); + } + "summary" => { + curr_instruction_form.avr_summary = + Some(ustr::get_str(&value).to_owned()); + } + _ => {} + } + } + } + // NOTE: Intentionally leaving out encoding unless that's desired... + // QName(b"Encoding") => {} + _ => {} // unknown event + } + } + Ok(Event::Empty(ref e)) => { + match e.name() { + QName(b"Operand") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"type" { + let val = ustr::get_str(&value); + for oper in val.split(',') { + if oper.is_empty() { + continue; + } + let Ok(type_) = OperandType::from_str(oper) else { + return Err(anyhow!( + "Unknown value for operand type -- Variant: {}", + ustr::get_str(&value) + )); + }; + curr_instruction_form.operands.push(Operand { + type_, + input: None, + output: None, + extended_size: None, + }); + } + } + } + } + // TODO: Clean this up... + // Status register + QName(b"I") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let val = ustr::get_str(&value); + let status = if val.eq("–") { + '-' + } else { + ustr::get_str(&value) + .chars() + .next() + .expect("Empty status register value") + }; + if let Some(ref mut sreg_entry) = + curr_instruction_form.avr_status_register + { + sreg_entry.i = status; + } else { + curr_instruction_form.avr_status_register = + Some(AvrStatusRegister { + i: status, + ..Default::default() + }); + } + } + } + } + QName(b"T") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let val = ustr::get_str(&value); + let status = if val.eq("–") { + '-' + } else { + ustr::get_str(&value) + .chars() + .next() + .expect("Empty status register value") + }; + if let Some(ref mut sreg_entry) = + curr_instruction_form.avr_status_register + { + sreg_entry.t = status; + } else { + curr_instruction_form.avr_status_register = + Some(AvrStatusRegister { + t: status, + ..Default::default() + }); + } + } + } + } + QName(b"H") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let val = ustr::get_str(&value); + let status = if val.eq("–") { + '-' + } else { + ustr::get_str(&value) + .chars() + .next() + .expect("Empty status register value") + }; + if let Some(ref mut sreg_entry) = + curr_instruction_form.avr_status_register + { + sreg_entry.h = status; + } else { + curr_instruction_form.avr_status_register = + Some(AvrStatusRegister { + h: status, + ..Default::default() + }); + } + } + } + } + QName(b"S") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let val = ustr::get_str(&value); + let status = if val.eq("–") { + '-' + } else { + ustr::get_str(&value) + .chars() + .next() + .expect("Empty status register value") + }; + if let Some(ref mut sreg_entry) = + curr_instruction_form.avr_status_register + { + sreg_entry.s = status; + } else { + curr_instruction_form.avr_status_register = + Some(AvrStatusRegister { + s: status, + ..Default::default() + }); + } + } + } + } + QName(b"V") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let val = ustr::get_str(&value); + let status = if val.eq("–") { + '-' + } else { + ustr::get_str(&value) + .chars() + .next() + .expect("Empty status register value") + }; + if let Some(ref mut sreg_entry) = + curr_instruction_form.avr_status_register + { + sreg_entry.v = status; + } else { + curr_instruction_form.avr_status_register = + Some(AvrStatusRegister { + v: status, + ..Default::default() + }); + } + } + } + } + QName(b"Z") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let val = ustr::get_str(&value); + let status = if val.eq("–") { + '-' + } else { + ustr::get_str(&value) + .chars() + .next() + .expect("Empty status register value") + }; + if let Some(ref mut sreg_entry) = + curr_instruction_form.avr_status_register + { + sreg_entry.z = status; + } else { + curr_instruction_form.avr_status_register = + Some(AvrStatusRegister { + z: status, + ..Default::default() + }); + } + } + } + } + QName(b"C") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let val = ustr::get_str(&value); + let status = if val.eq("–") { + '-' + } else { + ustr::get_str(&value) + .chars() + .next() + .expect("Empty status register value") + }; + if let Some(ref mut sreg_entry) = + curr_instruction_form.avr_status_register + { + sreg_entry.c = status; + } else { + curr_instruction_form.avr_status_register = + Some(AvrStatusRegister { + c: status, + ..Default::default() + }); + } + } + } + } + QName(b"N") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let val = ustr::get_str(&value); + let status = if val.eq("–") { + '-' + } else { + ustr::get_str(&value) + .chars() + .next() + .expect("Empty status register value") + }; + if let Some(ref mut sreg_entry) = + curr_instruction_form.avr_status_register + { + sreg_entry.n = status; + } else { + curr_instruction_form.avr_status_register = + Some(AvrStatusRegister { + n: status, + ..Default::default() + }); + } + } + } + } + // Clocks + QName(b"AVRe") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let cycles = Some(ustr::get_str(&value).to_string()); + if let Some(ref mut timing_entry) = curr_instruction_form.avr_timing + { + timing_entry.avre = cycles; + } else { + curr_instruction_form.avr_timing = Some(AvrTiming { + avre: cycles, + ..Default::default() + }); + } + } + } + } + QName(b"AVRxm") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let cycles = Some(ustr::get_str(&value).to_string()); + if let Some(ref mut timing_entry) = curr_instruction_form.avr_timing + { + timing_entry.avrxm = cycles; + } else { + curr_instruction_form.avr_timing = Some(AvrTiming { + avrxm: cycles, + ..Default::default() + }); + } + } + } + } + QName(b"AVRxt") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let cycles = Some(ustr::get_str(&value).to_string()); + if let Some(ref mut timing_entry) = curr_instruction_form.avr_timing + { + timing_entry.avrxt = cycles; + } else { + curr_instruction_form.avr_timing = Some(AvrTiming { + avrxt: cycles, + ..Default::default() + }); + } + } + } + } + QName(b"AVRrc") => { + for attr in e.attributes() { + let Attribute { key, value } = attr.unwrap(); + if key.into_inner() == b"value" { + let cycles = Some(ustr::get_str(&value).to_string()); + if let Some(ref mut timing_entry) = curr_instruction_form.avr_timing + { + timing_entry.avrrc = cycles; + } else { + curr_instruction_form.avr_timing = Some(AvrTiming { + avrrc: cycles, + ..Default::default() + }); + } + } + } + } + _ => {} // unknown event + } + } + // end event + Ok(Event::End(ref e)) => { + match e.name() { + QName(b"Instruction") => { + // finish instruction + assert!(curr_instruction.arch != Arch::None); + instructions_map + .insert(curr_instruction.name.clone(), curr_instruction.clone()); + curr_version = None; + } + QName(b"InstructionForm") => { + curr_instruction.push_form(curr_instruction_form.clone()); + } + _ => {} // unknown event + } + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + _ => {} // rest of events that we don't consider + } + } + + Ok(instructions_map.into_values().collect()) +} + /// Parse the provided XML contents and return a vector of all the registers based on that. /// If parsing fails, the appropriate error will be returned instead. /// diff --git a/asm-lsp/serialized/directives/avr b/asm-lsp/serialized/directives/avr index a7da5dbd..c74abb61 100644 Binary files a/asm-lsp/serialized/directives/avr and b/asm-lsp/serialized/directives/avr differ diff --git a/asm-lsp/serialized/directives/gas b/asm-lsp/serialized/directives/gas index 84f30429..7e296ac6 100644 Binary files a/asm-lsp/serialized/directives/gas and b/asm-lsp/serialized/directives/gas differ diff --git a/asm-lsp/serialized/directives/masm b/asm-lsp/serialized/directives/masm index 177f2f6b..d78bb110 100644 Binary files a/asm-lsp/serialized/directives/masm and b/asm-lsp/serialized/directives/masm differ diff --git a/asm-lsp/serialized/directives/nasm b/asm-lsp/serialized/directives/nasm index cb331bd7..92e80970 100644 Binary files a/asm-lsp/serialized/directives/nasm and b/asm-lsp/serialized/directives/nasm differ diff --git a/asm-lsp/serialized/opcodes/arm b/asm-lsp/serialized/opcodes/arm index 400c2778..ae2ea781 100644 Binary files a/asm-lsp/serialized/opcodes/arm and b/asm-lsp/serialized/opcodes/arm differ diff --git a/asm-lsp/serialized/opcodes/arm64 b/asm-lsp/serialized/opcodes/arm64 index bf12c86a..df911dcf 100644 Binary files a/asm-lsp/serialized/opcodes/arm64 and b/asm-lsp/serialized/opcodes/arm64 differ diff --git a/asm-lsp/serialized/opcodes/avr b/asm-lsp/serialized/opcodes/avr new file mode 100644 index 00000000..7936268e Binary files /dev/null and b/asm-lsp/serialized/opcodes/avr differ diff --git a/asm-lsp/serialized/opcodes/riscv b/asm-lsp/serialized/opcodes/riscv index 6e4224ea..cca0bab7 100644 Binary files a/asm-lsp/serialized/opcodes/riscv and b/asm-lsp/serialized/opcodes/riscv differ diff --git a/asm-lsp/serialized/opcodes/x86 b/asm-lsp/serialized/opcodes/x86 index 001f8935..1fcfcc12 100644 Binary files a/asm-lsp/serialized/opcodes/x86 and b/asm-lsp/serialized/opcodes/x86 differ diff --git a/asm-lsp/serialized/opcodes/x86_64 b/asm-lsp/serialized/opcodes/x86_64 index 1fefc7b9..b86b4862 100644 Binary files a/asm-lsp/serialized/opcodes/x86_64 and b/asm-lsp/serialized/opcodes/x86_64 differ diff --git a/asm-lsp/serialized/opcodes/z80 b/asm-lsp/serialized/opcodes/z80 index 141c23b5..4f0e68b1 100644 Binary files a/asm-lsp/serialized/opcodes/z80 and b/asm-lsp/serialized/opcodes/z80 differ diff --git a/asm-lsp/serialized/registers/6502 b/asm-lsp/serialized/registers/6502 index 921c29a8..b9c0ed21 100644 Binary files a/asm-lsp/serialized/registers/6502 and b/asm-lsp/serialized/registers/6502 differ diff --git a/asm-lsp/serialized/registers/arm b/asm-lsp/serialized/registers/arm index d40a98d8..5e1fd7bf 100644 Binary files a/asm-lsp/serialized/registers/arm and b/asm-lsp/serialized/registers/arm differ diff --git a/asm-lsp/serialized/registers/arm64 b/asm-lsp/serialized/registers/arm64 index 8e7f53c0..9beff4cd 100644 Binary files a/asm-lsp/serialized/registers/arm64 and b/asm-lsp/serialized/registers/arm64 differ diff --git a/asm-lsp/serialized/registers/avr b/asm-lsp/serialized/registers/avr index 2357f1b6..7493c88c 100644 Binary files a/asm-lsp/serialized/registers/avr and b/asm-lsp/serialized/registers/avr differ diff --git a/asm-lsp/serialized/registers/power-isa b/asm-lsp/serialized/registers/power-isa index cd81b304..5be5e0a1 100644 Binary files a/asm-lsp/serialized/registers/power-isa and b/asm-lsp/serialized/registers/power-isa differ diff --git a/asm-lsp/serialized/registers/x86 b/asm-lsp/serialized/registers/x86 index 7d774018..690b1b97 100644 Binary files a/asm-lsp/serialized/registers/x86 and b/asm-lsp/serialized/registers/x86 differ diff --git a/asm-lsp/serialized/registers/x86_64 b/asm-lsp/serialized/registers/x86_64 index 59ed362c..370d6b4e 100644 Binary files a/asm-lsp/serialized/registers/x86_64 and b/asm-lsp/serialized/registers/x86_64 differ diff --git a/asm-lsp/serialized/registers/z80 b/asm-lsp/serialized/registers/z80 index faa35964..fa6de060 100644 Binary files a/asm-lsp/serialized/registers/z80 and b/asm-lsp/serialized/registers/z80 differ diff --git a/asm-lsp/test.rs b/asm-lsp/test.rs index b66d8c53..ab87401d 100644 --- a/asm-lsp/test.rs +++ b/asm-lsp/test.rs @@ -185,6 +185,7 @@ mod tests { z80_registers: Vec, mos6502_registers: Vec, power_isa_registers: Vec, + avr_instructions: Vec, avr_registers: Vec, gas_directives: Vec, masm_directives: Vec, @@ -212,6 +213,7 @@ mod tests { z80_registers: Vec::new(), mos6502_instructions: Vec::new(), mos6502_registers: Vec::new(), + avr_instructions: Vec::new(), avr_registers: Vec::new(), gas_directives: Vec::new(), masm_directives: Vec::new(), @@ -295,6 +297,13 @@ mod tests { Vec::new() }; + info.avr_instructions = if config.is_isa_enabled(Arch::Avr) { + let avr_instrs = include_bytes!("serialized/opcodes/avr"); + bincode::deserialize::>(avr_instrs)? + } else { + Vec::new() + }; + info.x86_registers = if config.is_isa_enabled(Arch::X86) { let regs_x86 = include_bytes!("serialized/registers/x86"); bincode::deserialize(regs_x86)? @@ -447,6 +456,12 @@ mod tests { &mut store.names_to_info.instructions, ); + populate_name_to_instruction_map( + Arch::Avr, + &info.avr_instructions, + &mut store.names_to_info.instructions, + ); + populate_name_to_register_map( Arch::X86, &info.x86_registers, @@ -781,7 +796,7 @@ mod tests { fn handle_hover_avr_arch_it_provides_reg_info_1() { test_hover( "ldi r16,0xC0", - "R16 [avr] + "R16 [AVR] General purpose register 16. Mapped to address 0x10. Type: General Purpose Register @@ -793,7 +808,7 @@ Width: 8 bits", fn handle_hover_avr_arch_it_provides_reg_info_2() { test_hover( "clr r17", - "R17 [avr] + "R17 [AVR] General purpose register 17. Mapped to address 0x11. Type: General Purpose Register @@ -805,7 +820,7 @@ Width: 8 bits", fn handle_hover_avr_arch_it_provides_reg_info_3() { test_hover( "x", - "X [avr] + "X [AVR] General purpose X-register. Low byte is r26 and high byte is r27. Type: General Purpose Register @@ -814,6 +829,71 @@ Width: 16 bits", ); } #[test] + fn handle_hover_avr_arch_it_provides_instr_info_1() { + test_hover( + "ldi r16,0xC0", + "ldi [AVR] + +## Forms + +- *AVR*: LDI (All) + +Load Immediate + + [Rd] + + [K] + +I T H S V N Z C +- - - - - - - - + +Timing: AVRE: 1 | AVRXM: 1 | AVRXT: 1 | AVRRC: 1 +", + &avr_arch_test_config(), + ); + } + #[test] + fn handle_hover_avr_arch_it_provides_instr_info_2() { + test_hover( + "clr r17", + "clr [AVR] + +## Forms + +- *AVR*: CLR (All) + +Clear Register + + [Rd] + +I T H S V N Z C +- - - 0 0 0 - 1 + +Timing: AVRE: 1 | AVRXM: 1 | AVRXT: 1 | AVRRC: 1 +", + &avr_arch_test_config(), + ); + } + #[test] + fn handle_hover_avr_arch_it_provides_instr_info_3() { + test_hover( + " out 0x25,r16", + "out [AVR] + +## Forms + +- *AVR*: OUT (All) + +Out To I/O Location + + [A] + + [Rr] + +I T H S V N Z C +- - - - - - - - + +Timing: AVRE: 1 | AVRXM: 1 | AVRXT: 1 | AVRRC: 1 +", + &avr_arch_test_config(), + ); + } + #[test] fn handle_autocomplete_avr_arch_it_provides_reg_comps_1() { test_register_autocomplete( "ldi r", @@ -841,6 +921,33 @@ Width: 16 bits", ); } #[test] + fn handle_autocomplete_avr_arch_it_provides_instr_comps_1() { + test_instruction_autocomplete( + "l r1", + &avr_arch_test_config(), + CompletionTriggerKind::INVOKED, + None, + ); + } + #[test] + fn handle_autocomplete_avr_arch_it_provides_instr_comps_2() { + test_instruction_autocomplete( + "ld r1,0xC0", + &avr_arch_test_config(), + CompletionTriggerKind::INVOKED, + None, + ); + } + #[test] + fn handle_autocomplete_avr_arch_it_provides_instr_comps_3() { + test_instruction_autocomplete( + "f r16,r1", + &avr_arch_test_config(), + CompletionTriggerKind::INVOKED, + None, + ); + } + #[test] fn handle_autocomplete_avr_assembler_it_provides_dir_comps_no_args() { test_directive_autocomplete( ".und", diff --git a/asm-lsp/types.rs b/asm-lsp/types.rs index 4297cd32..2f0ac33c 100644 --- a/asm-lsp/types.rs +++ b/asm-lsp/types.rs @@ -149,6 +149,7 @@ impl<'own> Instruction { } } +// TODO: Rework this to use a tagged union... // InstructionForm #[derive(Default, Eq, PartialEq, Hash, Debug, Clone, Serialize, Deserialize)] pub struct InstructionForm { @@ -160,14 +161,20 @@ pub struct InstructionForm { pub cancelling_inputs: Option, pub nacl_version: Option, pub nacl_zero_extends_outputs: Option, - pub operands: Vec, // --- Z80-Specific Information --- pub z80_name: Option, pub z80_form: Option, pub z80_opcode: Option, pub z80_timing: Option, + // --- Avr-Specific Information --- + pub avr_mneumonic: Option, + pub avr_summary: Option, + pub avr_version: Option, + pub avr_timing: Option, + pub avr_status_register: Option, // --- Assembler/Architecture Agnostic Info --- pub isa: Option, + pub operands: Vec, pub urls: Vec, } @@ -183,6 +190,13 @@ impl std::fmt::Display for InstructionForm { if let Some(val) = &self.z80_form { s += &format!("*Z80*: {val} | "); } + if let Some(val) = &self.avr_mneumonic { + let version_str = self + .avr_version + .as_ref() + .map_or_else(String::new, |version| format!(" ({version})")); + s += &format!("*AVR*: {val}{version_str} | ",); + } if let Some(val) = &self.mmx_mode { s += &(format!("*MMX*: {} | ", val.as_ref())); @@ -211,6 +225,10 @@ impl std::fmt::Display for InstructionForm { s = format!("- {}\n\n", &s[..s.len() - 3]); } + if let Some(summary) = &self.avr_summary { + s += &format!("\n{summary}\n"); + } + // Operands let operands_str: String = if self.operands.is_empty() { String::new() @@ -241,6 +259,13 @@ impl std::fmt::Display for InstructionForm { s += &format!("\n + {timing}"); } + if let Some(sreg) = &self.avr_status_register { + s += &format!("\n\n{sreg}\n"); + } + if let Some(ref timing) = self.avr_timing { + s += &format!("\n\n{timing}\n"); + } + for url in &self.urls { s += &format!("\n + More info: {url}\n"); } @@ -388,6 +413,71 @@ impl Display for Z80Timing { } } +#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct AvrTiming { + pub avre: Option, + pub avrxm: Option, + pub avrxt: Option, + pub avrrc: Option, +} + +impl Display for AvrTiming { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Timing: ")?; + let mut has_prev = false; + if let Some(ref cycles) = self.avre { + write!(f, "AVRE: {cycles}")?; + has_prev = true; + } + if let Some(ref cycles) = self.avrxm { + if has_prev { + write!(f, " | ")?; + } + write!(f, "AVRXM: {cycles}")?; + has_prev = true; + } + if let Some(ref cycles) = self.avrxt { + if has_prev { + write!(f, " | ")?; + } + write!(f, "AVRXT: {cycles}")?; + has_prev = true; + } + if let Some(ref cycles) = self.avrrc { + if has_prev { + write!(f, " | ")?; + } + write!(f, "AVRRC: {cycles}")?; + } + + Ok(()) + } +} + +#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct AvrStatusRegister { + pub i: char, + pub t: char, + pub h: char, + pub s: char, + pub v: char, + pub n: char, + pub c: char, + pub z: char, +} + +impl std::fmt::Display for AvrStatusRegister { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "I T H S V N Z C")?; + writeln!( + f, + "{} {} {} {} {} {} {} {}", + self.i, self.t, self.h, self.s, self.v, self.n, self.c, self.z + )?; + Ok(()) + } +} + // Directive #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Directive { @@ -669,8 +759,8 @@ pub enum Arch { #[strum(serialize = "power-isa")] #[serde(rename = "power-isa")] PowerISA, - #[strum(serialize = "avr")] - #[serde(rename = "avr")] + #[strum(serialize = "AVR")] + #[serde(rename = "AVR")] // TODO: lower-case this in the generation code Avr, /// For testing purposes *only*. This is not a valid config option #[serde(skip)] @@ -766,7 +856,7 @@ impl Arch { Self::PowerISA => { load_instructions_with_path!(Self::PowerISA, "serialized/opcodes/power-isa"); } - Self::Avr => warn!("AVR opcodes are not supported"), + Self::Avr => load_instructions_with_path!(Self::Avr, "serialized/opcodes/avr"), Self::None => unreachable!(), } } @@ -1520,6 +1610,34 @@ pub enum OperandType { sae, sibmem, tmm, + + // Avr operand types + Rd, + Rr, + X, + Y, + Z, + #[strum(serialize = "-X")] + NegX, + #[strum(serialize = "-Y")] + NegY, + #[strum(serialize = "-Z")] + NegZ, + #[strum(serialize = "X+")] + XPlus, + #[strum(serialize = "Y+")] + YPlus, + #[strum(serialize = "Z+")] + ZPlus, + #[strum(serialize = "Y+q")] + YPlusQ, + #[strum(serialize = "Z+q")] + ZPlusQ, + A, + K, + // `k` is already covered + s, + b, } // lsp types diff --git a/asm_docs_parsing/regenerate.sh b/asm_docs_parsing/regenerate.sh index d9d7e1ad..5e33fde7 100755 --- a/asm_docs_parsing/regenerate.sh +++ b/asm_docs_parsing/regenerate.sh @@ -17,6 +17,7 @@ cargo build --release ../target/release/asm_docs_parsing ../docs_store/opcodes/z80.xml -o ../asm-lsp/serialized/opcodes/z80 --doc-type instruction ../target/release/asm_docs_parsing ../docs_store/opcodes/6502.html -o ../asm-lsp/serialized/opcodes/6502 --doc-type instruction --arch 6502 ../target/release/asm_docs_parsing ../docs_store/opcodes/power-isa.json -o ../asm-lsp/serialized/opcodes/power-isa --doc-type instruction --arch power-isa +../target/release/asm_docs_parsing ../docs_store/opcodes/avr.xml -o ../asm-lsp/serialized/opcodes/avr --doc-type instruction --arch AVR # register binaries ../target/release/asm_docs_parsing ../docs_store/registers/x86.xml -o ../asm-lsp/serialized/registers/x86 --doc-type register --arch x86 @@ -27,7 +28,7 @@ cargo build --release ../target/release/asm_docs_parsing ../docs_store/registers/z80.xml -o ../asm-lsp/serialized/registers/z80 --doc-type register --arch z80 ../target/release/asm_docs_parsing ../docs_store/registers/6502.xml -o ../asm-lsp/serialized/registers/6502 --doc-type register --arch 6502 ../target/release/asm_docs_parsing ../docs_store/registers/power-isa.xml -o ../asm-lsp/serialized/registers/power-isa --doc-type register --arch power-isa -../target/release/asm_docs_parsing ../docs_store/registers/avr.xml -o ../asm-lsp/serialized/registers/avr --doc-type register --arch avr +../target/release/asm_docs_parsing ../docs_store/registers/avr.xml -o ../asm-lsp/serialized/registers/avr --doc-type register --arch AVR # directive binaries ../target/release/asm_docs_parsing ../docs_store/directives/gas.xml -o ../asm-lsp/serialized/directives/gas --doc-type directive --assembler gas diff --git a/asm_docs_parsing/src/main.rs b/asm_docs_parsing/src/main.rs index 631a81e0..2fb8acf1 100644 --- a/asm_docs_parsing/src/main.rs +++ b/asm_docs_parsing/src/main.rs @@ -6,7 +6,7 @@ use ::asm_lsp::parser::{ populate_registers, populate_riscv_instructions, populate_riscv_registers, }; use asm_lsp::{ - parser::{populate_avr_directives, populate_power_isa_instructions}, + parser::{populate_avr_directives, populate_avr_instructions, populate_power_isa_instructions}, Arch, Assembler, Directive, Instruction, Register, }; @@ -91,6 +91,9 @@ fn run(opts: &SerializeDocs) -> Result<()> { Some(Arch::PowerISA) => { instrs = populate_power_isa_instructions(&conts)?; } + Some(Arch::Avr) => { + instrs = populate_avr_instructions(&conts)?; + } _ => { instrs = populate_instructions(&conts)?; } diff --git a/docs_store/registers/avr.xml b/docs_store/registers/avr.xml index c0fbe81d..91235bd6 100644 --- a/docs_store/registers/avr.xml +++ b/docs_store/registers/avr.xml @@ -1,5 +1,5 @@ - +