diff --git a/Cargo.toml b/Cargo.toml index 20f96f69..81729964 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "customasm" -version = "0.10.0" +version = "0.10.1" edition = "2018" authors = ["Henrique Lorenzi "] diff --git a/README.md b/README.md index 6ea8bab2..b208ba93 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,9 @@ Usage: customasm [options] ... Options: -f, --format FORMAT The format of the output file. Possible formats: - binary, binstr, hexstr, bindump, hexdump, - mif, intelhex, deccomma, hexcomma, decc, hexc, - logisim8, logisim16 + binary, annotated, annotatedbin, binstr, hexstr, + bindump, hexdump, mif, intelhex, deccomma, hexcomma, + decc, hexc, logisim8, logisim16 -o, --output FILE The name of the output file. -p, --print Print output to stdout instead of writing to a file. @@ -73,12 +73,16 @@ multiply3x4: ...the assembler would use the `#cpudef` rules to convert the instructions into binary code: -``` -0x0100: 11 00 ; load r1, 0 -0x0102: 12 03 ; load r2, 3 -0x0104: 13 04 ; load r3, 4 -0x0106: 21 ; add r1, r2 -0x0107: 33 01 ; sub r3, 1 -0x0109: 40 01 06 ; jnz .loop -0x010c: 50 ; ret +```asm + outp | addr | data + + 100:0 | 100 | ; multiply3x4: + 100:0 | 100 | 11 00 ; load r1, 0 + 102:0 | 102 | 12 03 ; load r2, 3 + 104:0 | 104 | 13 04 ; load r3, 4 + 106:0 | 106 | ; .loop: + 106:0 | 106 | 21 ; add r1, r2 + 107:0 | 107 | 33 01 ; sub r3, 1 + 109:0 | 109 | 40 01 06 ; jnz .loop + 10c:0 | 10c | 50 ; ret ``` diff --git a/examples/nes/ines_nrom.asm b/examples/nes/ines_nrom.asm index 4a61f719..93d16f8a 100644 --- a/examples/nes/ines_nrom.asm +++ b/examples/nes/ines_nrom.asm @@ -1,6 +1,6 @@ #bankdef "header" { #addr 0x0, #size 0x10, #outp 0x0 } #bankdef "prg" { #addr 0x8000, #size 0x7ffa, #outp 0x10 } -#bankdef "vectors" { #addr 0x7ffa, #size 0x6, #outp 0x800a } +#bankdef "vectors" { #addr 0xfffa, #size 0x6, #outp 0x800a } #bankdef "zeropage" { #addr 0x0, #size 0x100 } #bankdef "ram" { #addr 0x200, #size 0x600 } diff --git a/src/asm/assembler.rs b/src/asm/assembler.rs index 8e2bcad4..db5c0108 100644 --- a/src/asm/assembler.rs +++ b/src/asm/assembler.rs @@ -1,7 +1,8 @@ use crate::diagn::{Span, RcReport}; use crate::expr::{Expression, ExpressionValue, ExpressionEvalContext}; -use crate::asm::{AssemblerParser, BinaryOutput, LabelManager, LabelContext}; +use crate::asm::{AssemblerParser, LabelManager, LabelContext}; use crate::asm::BankDef; +use crate::asm::Bank; use crate::asm::BinaryBlock; use crate::asm::cpudef::CpuDef; use crate::util::FileServer; @@ -16,7 +17,7 @@ pub struct AssemblerState pub parsed_exprs: Vec, pub bankdefs: Vec, - pub blocks: Vec, + pub blocks: Vec, pub cur_bank: usize, pub cur_block: usize } @@ -48,6 +49,16 @@ pub struct ParsedExpression } +#[derive(Debug, Clone, PartialEq)] +pub struct BitRangeSpan +{ + pub start: usize, + pub end: usize, + pub addr: usize, + pub span: Span +} + + impl AssemblerState { pub fn new() -> AssemblerState @@ -66,7 +77,7 @@ impl AssemblerState }; state.bankdefs.push(BankDef::new("", 0, 0, Some(0), false, None)); - state.blocks.push(BinaryBlock::new("")); + state.blocks.push(Bank::new("")); state } @@ -90,6 +101,9 @@ impl AssemblerState self.resolve_exprs(report.clone())?; self.check_bank_overlap(report.clone()); + //for block in &self.blocks + // { println!("{:#?}", block.bits.spans); } + match report.has_errors() { true => Err(()), @@ -98,9 +112,9 @@ impl AssemblerState } - pub fn get_binary_output(&self) -> BinaryOutput + pub fn get_binary_output(&self) -> BinaryBlock { - let mut output = BinaryOutput::new(); + let mut output = BinaryBlock::new(); for block in &self.blocks { @@ -114,13 +128,32 @@ impl AssemblerState if let Some(output_index) = bankdef.outp { - for i in 0..block.len() - { output.write(output_index * bits + i, block.read(i)); } + let mut sorted_spans = block.bits.spans.clone(); + sorted_spans.sort_by(|a, b| a.start.cmp(&b.start)); + + let mut sorted_span_index = 0; + + for i in 0..block.bits.len() + { + let bitrange_index = sorted_spans[sorted_span_index..].iter().position(|s| i >= s.start && i < s.end); + let bitrange = bitrange_index.map(|i| &sorted_spans[sorted_span_index + i]); + + if let Some(bitrange_index) = bitrange_index + { sorted_span_index += bitrange_index; } + + if let Some(bitrange) = bitrange + { output.write(output_index * bits + i, block.bits.read(i), Some((bitrange.addr, &bitrange.span))); } + else + { output.write(output_index * bits + i, block.bits.read(i), None); } + } + + for bitrange in block.bits.spans.iter().filter(|s| s.start == s.end) + { output.mark_label(output_index * bits + bitrange.start, bitrange.addr, &bitrange.span); } if bankdef.fill { - for i in block.len()..(bankdef.size * bits) - { output.write(output_index * bits + i, false); } + for i in block.bits.len()..(bankdef.size * bits) + { output.write(output_index * bits + i, false, None); } } } } @@ -148,7 +181,7 @@ impl AssemblerState ExpressionContext { block: self.cur_block, - offset: block.len(), + offset: block.bits.len(), label_ctx: self.labels.get_cur_context() } } @@ -195,7 +228,7 @@ impl AssemblerState let bits = self.cpudef.as_ref().unwrap().bits; let block = &self.blocks[self.cur_block]; - let excess_bits = block.len() % bits; + let excess_bits = block.bits.len() % bits; if excess_bits != 0 { let bits_short = bits - excess_bits; @@ -206,7 +239,7 @@ impl AssemblerState let bankdef_index = self.find_bankdef(&block.bank_name).unwrap(); let bankdef = &self.bankdefs[bankdef_index]; - let block_offset = block.len() / bits; + let block_offset = block.bits.len() / bits; let addr = match block_offset.checked_add(bankdef.addr) { Some(addr) => addr, @@ -220,6 +253,28 @@ impl AssemblerState } + fn get_bitrange_address(&self, report: RcReport, span: &Span) -> Result + { + if self.cpudef.is_none() + { return Ok(0); } + + let bits = self.cpudef.as_ref().unwrap().bits; + let block = &self.blocks[self.cur_block]; + + let bankdef_index = self.find_bankdef(&block.bank_name).unwrap(); + let bankdef = &self.bankdefs[bankdef_index]; + + let block_offset = block.bits.len() / bits; + let addr = match block_offset.checked_add(bankdef.addr) + { + Some(addr) => addr, + None => return Err(report.error_span("address overflowed valid range", span)) + }; + + Ok(addr) + } + + pub fn check_valid_address(&self, report: RcReport, block_index: usize, addr: usize, span: &Span) -> Result<(), ()> { let block = &self.blocks[block_index]; @@ -236,52 +291,69 @@ impl AssemblerState } - pub fn output_bits_until_aligned(&mut self, report: RcReport, multiple_of: usize, span: &Span) -> Result<(), ()> + pub fn output_bits_until_aligned(&mut self, report: RcReport, multiple_of: usize, report_span: &Span, output_span: Option<&Span>) -> Result<(), ()> { if multiple_of == 0 - { return Err(report.error_span("invalid alignment", span)); } + { return Err(report.error_span("invalid alignment", report_span)); } - self.check_cpudef_active(report.clone(), span)?; + self.check_cpudef_active(report.clone(), report_span)?; let bits = self.cpudef.as_ref().unwrap().bits; - while self.blocks[self.cur_block].len() % (bits * multiple_of) != 0 - { self.output_bit(report.clone(), false, true, span)?; } + while self.blocks[self.cur_block].bits.len() % (bits * multiple_of) != 0 + { self.output_bit(report.clone(), false, true, report_span, output_span)?; } Ok(()) } - pub fn output_bit(&mut self, report: RcReport, bit: bool, skipping: bool, span: &Span) -> Result<(), ()> + pub fn output_bit(&mut self, report: RcReport, bit: bool, skipping: bool, report_span: &Span, output_span: Option<&Span>) -> Result<(), ()> { { let block = &self.blocks[self.cur_block]; let bankdef = &self.bankdefs[self.cur_bank]; if bankdef.outp.is_none() && !skipping - { return Err(report.error_span("attempt to place data in non-writable bank", span)); } + { return Err(report.error_span("attempt to place data in non-writable bank", report_span)); } if self.cur_bank != 0 { - self.check_cpudef_active(report.clone(), span)?; + self.check_cpudef_active(report.clone(), report_span)?; - if block.len() / self.cpudef.as_ref().unwrap().bits >= bankdef.size - { return Err(report.error_span("data overflowed bank size", span)); } + if block.bits.len() / self.cpudef.as_ref().unwrap().bits >= bankdef.size + { return Err(report.error_span("data overflowed bank size", report_span)); } } } - self.blocks[self.cur_block].append(bit); + let bitrange = match output_span + { + Some(output_span) => + { + let addr = self.get_bitrange_address(report, output_span)?; + Some((addr, output_span)) + } + None => None + }; + + self.blocks[self.cur_block].bits.append(bit, bitrange); Ok(()) } - pub fn output_zero_bits(&mut self, report: RcReport, num: usize, skipping: bool, span: &Span) -> Result<(), ()> + pub fn output_zero_bits(&mut self, report: RcReport, num: usize, skipping: bool, report_span: &Span, output_span: Option<&Span>) -> Result<(), ()> { for _ in 0..num - { self.output_bit(report.clone(), false, skipping, span)?; } + { self.output_bit(report.clone(), false, skipping, report_span, output_span)?; } Ok(()) } + + + pub fn mark_label(&mut self, addr: usize, output_span: &Span) + { + let index = self.blocks[self.cur_block].bits.len(); + self.blocks[self.cur_block].bits.mark_label(index, addr, output_span); + } pub fn resolve_instrs(&mut self, report: RcReport) -> Result<(), ()> @@ -342,12 +414,14 @@ impl AssemblerState let value = self.expr_eval(report.clone(), &instr.ctx, &rule.production, &mut args_eval_ctx)?; + let addr = self.get_bitrange_address(report, &instr.span)?; + let block = &mut self.blocks[instr.ctx.block]; for i in 0..(left - right + 1) { let bit = value.get_bit(left - right - i); - block.write(instr.ctx.offset + i, bit); + block.bits.write(instr.ctx.offset + i, bit, Some((addr, &instr.span))); } Ok(()) @@ -374,7 +448,7 @@ impl AssemblerState for i in 0..expr.width { let bit = value.get_bit(expr.width - i - 1); - block.write(expr.ctx.offset + i, bit); + block.bits.write(expr.ctx.offset + i, bit, None); } Ok(()) @@ -455,7 +529,7 @@ impl ExpressionContext let bits = state.cpudef.as_ref().unwrap().bits; let block = &state.blocks[self.block]; - if block.len() % bits != 0 + if block.bits.len() % bits != 0 { return Err({ report.error_span("address is not aligned to a byte", span); true }); } let bankdef = state.find_bankdef(&block.bank_name).unwrap(); diff --git a/src/asm/bank.rs b/src/asm/bank.rs new file mode 100644 index 00000000..dc0ae2fe --- /dev/null +++ b/src/asm/bank.rs @@ -0,0 +1,22 @@ +use crate::asm::BinaryBlock; + + +pub struct Bank +{ + pub bank_name: String, + pub bits: BinaryBlock +} + + +impl Bank +{ + pub fn new(bank_name: S) -> Bank + where S: Into + { + Bank + { + bank_name: bank_name.into(), + bits: BinaryBlock::new() + } + } +} \ No newline at end of file diff --git a/src/asm/binary_block.rs b/src/asm/binary_block.rs index 56493729..fbac59cc 100644 --- a/src/asm/binary_block.rs +++ b/src/asm/binary_block.rs @@ -1,52 +1,79 @@ +use crate::asm::BitRangeSpan; +use crate::diagn::Span; +use crate::diagn::RcReport; +use crate::util::FileServer; +use crate::util::CharCounter; + + pub struct BinaryBlock { - pub bank_name: String, - pub bits: Vec + pub bits: Vec, + pub spans: Vec } impl BinaryBlock { - pub fn new(bank_name: S) -> BinaryBlock - where S: Into + pub fn new() -> BinaryBlock { BinaryBlock { - bank_name: bank_name.into(), - bits: Vec::new() + bits: Vec::new(), + spans: Vec::new() } } - pub fn append(&mut self, bit: bool) + pub fn append(&mut self, bit: bool, maybe_range: Option<(usize, &Span)>) { let index = self.len(); - self.write(index, bit); - } - - - pub fn truncate(&mut self, new_len: usize) - { - while self.bits.len() > new_len - { self.bits.pop(); } + self.write(index, bit, maybe_range); } - pub fn write(&mut self, index: usize, bit: bool) + pub fn write(&mut self, index: usize, bit: bool, maybe_range: Option<(usize, &Span)>) { while self.bits.len() <= index { self.bits.push(false); } self.bits[index] = bit; + + if let Some(range) = maybe_range + { + let spans_len = self.spans.len(); + + let addr = range.0; + let span = range.1; + + if self.spans.len() > 0 && *span == self.spans[spans_len - 1].span && index == self.spans[spans_len - 1].end + { self.spans[spans_len - 1].end = index + 1; } + else + { + self.spans.push(BitRangeSpan + { + start: index, + end: index + 1, + addr, + span: span.clone() + }) + } + } } - pub fn read(&self, index: usize) -> bool + pub fn read(&self, bit_index: usize) -> bool { - if index >= self.bits.len() + if bit_index >= self.bits.len() { false } else - { self.bits[index] } + { self.bits[bit_index] } + } + + + pub fn truncate(&mut self, new_len: usize) + { + while self.bits.len() > new_len + { self.bits.pop(); } } @@ -54,4 +81,522 @@ impl BinaryBlock { self.bits.len() } + + + pub fn mark_label(&mut self, index: usize, addr: usize, span: &Span) + { + self.spans.push(BitRangeSpan + { + start: index, + end: index, + addr, + span: span.clone() + }) + } + + + pub fn generate_binstr(&self, start_bit: usize, end_bit: usize) -> String + { + self.generate_str(1, start_bit, end_bit) + } + + + pub fn generate_hexstr(&self, start_bit: usize, end_bit: usize) -> String + { + self.generate_str(4, start_bit, end_bit) + } + + + pub fn generate_str(&self, digit_bits: usize, start_bit: usize, end_bit: usize) -> String + { + let mut result = String::new(); + + let mut index = start_bit; + while index < end_bit + { + let mut digit: u8 = 0; + for _ in 0..digit_bits + { + digit <<= 1; + digit |= if self.read(index) { 1 } else { 0 }; + index += 1; + } + + let c = if digit < 10 + { ('0' as u8 + digit) as char } + else + { ('a' as u8 + digit - 10) as char }; + + result.push(c); + } + + result + } + + + pub fn generate_bindump(&self, start_bit: usize, end_bit: usize) -> String + { + self.generate_dump(1, start_bit, end_bit, 8, 8) + } + + + pub fn generate_hexdump(&self, start_bit: usize, end_bit: usize) -> String + { + self.generate_dump(4, start_bit, end_bit, 8, 16) + } + + + pub fn generate_dump(&self, digit_bits: usize, start_bit: usize, end_bit: usize, byte_bits: usize, bytes_per_line: usize) -> String + { + let mut result = String::new(); + + let line_start = start_bit / (byte_bits * bytes_per_line); + let line_end = (end_bit + (bytes_per_line - 1) * byte_bits) / (byte_bits * bytes_per_line); + + let line_end = if end_bit - start_bit < byte_bits { line_start + 1 } else { line_end }; + + let addr_max_width = format!("{:x}", (line_end - 1) * bytes_per_line).len(); + + for line_index in line_start..line_end + { + result.push_str(&format!(" {:01$x} | ", line_index * bytes_per_line, addr_max_width)); + + for byte_index in 0..bytes_per_line + { + for digit_index in 0..(byte_bits / digit_bits) + { + let digit_first_bit = (line_index * bytes_per_line + byte_index) * byte_bits + digit_index * digit_bits; + + if digit_first_bit >= self.len() + { + result.push('.'); + continue; + } + + let mut digit = 0; + for bit_index in 0..digit_bits + { + digit <<= 1; + digit |= if self.read(digit_first_bit + bit_index) { 1 } else { 0 }; + } + + let c = if digit < 10 + { ('0' as u8 + digit) as char } + else + { ('a' as u8 + digit - 10) as char }; + + result.push(c); + } + + result.push(' '); + + if byte_index % 4 == 3 && byte_index < bytes_per_line - 1 + { result.push(' '); } + } + + result.push_str("| "); + + if byte_bits == 8 + { + for byte_index in 0..bytes_per_line + { + let byte_first_bit = (line_index * bytes_per_line + byte_index) * byte_bits; + + if byte_first_bit >= self.len() + { + result.push('.'); + continue; + } + + let mut byte = 0u8; + for bit_index in 0..byte_bits + { + byte <<= 1; + byte |= if self.read(byte_first_bit + bit_index) { 1 } else { 0 }; + } + + let c = byte as char; + + if c == ' ' || c == '\t' || c == '\r' || c == '\n' + { result.push(' '); } + else if c as u8 >= 0x80 || c < ' ' || c == '|' + { result.push('.'); } + else + { result.push(c); } + } + + result.push_str(" |"); + } + + result.push('\n'); + } + + result + } + + + pub fn generate_binary(&self, start_bit: usize, end_bit: usize) -> Vec + { + let mut result = Vec::new(); + + let mut index = start_bit; + while index < end_bit + { + let mut byte: u8 = 0; + for _ in 0..8 + { + byte <<= 1; + byte |= if self.read(index) { 1 } else { 0 }; + index += 1; + } + + result.push(byte); + } + + result + } + + + pub fn generate_mif(&self, start_bit: usize, end_bit: usize) -> String + { + let mut result = String::new(); + + let byte_num = (end_bit - start_bit) / 8 + if (end_bit - start_bit) % 8 != 0 { 1 } else { 0 }; + + result.push_str(&format!("DEPTH = {};\n", byte_num)); + result.push_str("WIDTH = 8;\n"); + result.push_str("ADDRESS_RADIX = HEX;\n"); + result.push_str("DATA_RADIX = HEX;\n"); + result.push_str("\n"); + result.push_str("CONTENT\n"); + result.push_str("BEGIN\n"); + + let addr_max_width = format!("{:x}", byte_num - 1).len(); + + let mut index = start_bit; + while index < end_bit + { + result.push_str(&format!(" {:1$X}: ", index / 8, addr_max_width)); + + let mut byte: u8 = 0; + for _ in 0..8 + { + byte <<= 1; + byte |= if self.read(index) { 1 } else { 0 }; + index += 1; + } + + result.push_str(&format!("{:02X};\n", byte)); + } + + result.push_str("END;"); + result + } + + + pub fn generate_intelhex(&self, start_bit: usize, end_bit: usize) -> String + { + let mut result = String::new(); + + let mut bytes_left = (end_bit - start_bit) / 8 + if (end_bit - start_bit) % 8 != 0 { 1 } else { 0 }; + + let mut index = start_bit; + while index < end_bit + { + let bytes_in_row = if bytes_left > 32 { 32 } else { bytes_left }; + + result.push(':'); + result.push_str(&format!("{:02X}", bytes_in_row)); + result.push_str(&format!("{:04X}", index / 8)); + result.push_str("00"); + + let mut checksum = 0_u8; + checksum = checksum.wrapping_add(bytes_in_row as u8); + checksum = checksum.wrapping_add(((index / 8) >> 8) as u8); + checksum = checksum.wrapping_add((index / 8) as u8); + + for _ in 0..bytes_in_row + { + let mut byte: u8 = 0; + for _ in 0..8 + { + byte <<= 1; + byte |= if self.read(index) { 1 } else { 0 }; + index += 1; + } + + result.push_str(&format!("{:02X}", byte)); + checksum = checksum.wrapping_add(byte); + } + + bytes_left -= bytes_in_row; + result.push_str(&format!("{:02X}", (!checksum).wrapping_add(1))); + result.push('\n'); + } + + result.push_str(":00000001FF"); + result + } + + + pub fn generate_comma(&self, start_bit: usize, end_bit: usize, radix: usize) -> String + { + let mut result = String::new(); + + let mut index = start_bit; + while index < end_bit + { + let mut byte: u8 = 0; + for _ in 0..8 + { + byte <<= 1; + byte |= if self.read(index) { 1 } else { 0 }; + index += 1; + } + + match radix + { + 10 => result.push_str(&format!("{}", byte)), + 16 => result.push_str(&format!("0x{:02x}", byte)), + _ => panic!("invalid radix") + } + + if index < end_bit + { + result.push_str(", "); + + if (index / 8) % 16 == 0 + { result.push('\n'); } + } + } + + result + } + + + pub fn generate_c_array(&self, start_bit: usize, end_bit: usize, radix: usize) -> String + { + let mut result = String::new(); + + result.push_str("const unsigned char data[] = {\n"); + + let byte_num = (end_bit - start_bit) / 8 + if (end_bit - start_bit) % 8 != 0 { 1 } else { 0 }; + let addr_max_width = format!("{:x}", byte_num - 1).len(); + + let mut index = start_bit; + result.push_str(&format!("\t/* 0x{:01$x} */ ", 0, addr_max_width)); + + while index < end_bit + { + let mut byte: u8 = 0; + for _ in 0..8 + { + byte <<= 1; + byte |= if self.read(index) { 1 } else { 0 }; + index += 1; + } + + match radix + { + 10 => result.push_str(&format!("{}", byte)), + 16 => result.push_str(&format!("0x{:02x}", byte)), + _ => panic!("invalid radix") + } + + if index < end_bit + { + result.push_str(", "); + + if (index / 8) % 16 == 0 + { + result.push_str(&format!("\n\t/* 0x{:01$x} */ ", index / 8, addr_max_width)); + } + } + } + + result.push_str("\n};"); + result + } + + + // From: https://github.com/milanvidakovic/customasm/blob/master/src/asm/binary_output.rs#L84 + pub fn generate_logisim(&self, start_bit: usize, end_bit: usize, bits_per_chunk: usize) -> String + { + let mut result = String::new(); + result.push_str("v2.0 raw\n"); + + let mut index = start_bit; + while index < end_bit + { + let mut value: u16 = 0; + for _ in 0..bits_per_chunk + { + value <<= 1; + value |= if self.read(index) { 1 } else { 0 }; + index += 1; + } + + result.push_str(&format!("{:01$x} ", value, bits_per_chunk / 4)); + if (index / 8) % 16 == 0 + { result.push('\n'); } + } + + result + } + + + pub fn generate_annotated_bin(&self, fileserver: &dyn FileServer, start_bit: usize, end_bit: usize) -> String + { + self.generate_annotated(fileserver, 1, start_bit, end_bit, 8) + } + + + pub fn generate_annotated_hex(&self, fileserver: &dyn FileServer, start_bit: usize, end_bit: usize) -> String + { + self.generate_annotated(fileserver, 4, start_bit, end_bit, 2) + } + + + pub fn generate_annotated(&self, fileserver: &dyn FileServer, digit_bits: usize, start_bit: usize, end_bit: usize, byte_digits: usize) -> String + { + let mut result = String::new(); + + let byte_bits = byte_digits * digit_bits; + + let mut outp_width = 2; + let outp_bit_width = format!("{:x}", digit_bits - 1).len(); + let mut addr_width = 4; + let content_width = (byte_digits + 1) * 5 - 1; + + let mut sorted_spans = self.spans.clone(); + sorted_spans.sort_by(|a, b| + if a.start == b.start + { (b.end - b.start).cmp(&(a.end - a.start)) } + else + { a.start.cmp(&b.start) } + ); + + //for bitrange in &sorted_spans + // { println!("{:?}", bitrange); } + + let mut sorted_span_index = 0; + + for index in start_bit..end_bit + { + let bitrange_index = sorted_spans[sorted_span_index..].iter().position(|s| index >= s.start && index < s.end); + let bitrange = bitrange_index.map(|i| &sorted_spans[sorted_span_index + i]); + + if let Some(bitrange_index) = bitrange_index + { sorted_span_index += bitrange_index; } + + if let Some(bitrange) = bitrange + { + outp_width = std::cmp::max(outp_width, format!("{:x}", bitrange.start / byte_bits).len()); + addr_width = std::cmp::max(addr_width, format!("{:x}", bitrange.addr).len()); + } + } + + result.push_str(&format!(" {:>1$} |", "outp", outp_width + outp_bit_width + 1)); + result.push_str(&format!(" {:>1$} | data", "addr", addr_width)); + result.push_str("\n"); + result.push_str("\n"); + + let mut prev_bitrange = None; + let mut accum_bits = Vec::::new(); + + sorted_span_index = 0; + + let mut prev_filename = ""; + let mut prev_file_chars = Vec::new(); + + for index in start_bit..=end_bit + { + let bitrange_index = sorted_spans[sorted_span_index..].iter().position(|s| index >= s.start && index < s.end); + let bitrange = bitrange_index.map(|i| &sorted_spans[sorted_span_index + i]); + + if let Some(bitrange_index) = bitrange_index + { + for label_bitrange in &sorted_spans[sorted_span_index..(sorted_span_index + bitrange_index)] + { + if label_bitrange.start != label_bitrange.end + { continue; } + + if &*label_bitrange.span.file != prev_filename + { + prev_filename = &*label_bitrange.span.file; + prev_file_chars = fileserver.get_chars(RcReport::new(), &prev_filename, None).ok().unwrap(); + } + + let span_location = label_bitrange.span.location.unwrap(); + let char_counter = CharCounter::new(&prev_file_chars); + + result.push_str(&format!(" {:1$x}", label_bitrange.start / byte_bits, outp_width)); + result.push_str(&format!(":{:1$x} | ", label_bitrange.start % byte_bits, outp_bit_width)); + result.push_str(&format!("{:1$x} | ", label_bitrange.addr, addr_width)); + result.push_str(&format!("{:1$}", "", content_width)); + result.push_str(&format!(" ; {}", char_counter.get_excerpt(span_location.0, span_location.1).iter().collect::())); + result.push_str("\n"); + } + + sorted_span_index += bitrange_index; + } + + if prev_bitrange != bitrange + { + if let Some(prev_bitrange) = prev_bitrange + { + result.push_str(&format!(" {:1$x}", prev_bitrange.start / byte_bits, outp_width)); + result.push_str(&format!(":{:1$x} | ", prev_bitrange.start % byte_bits, outp_bit_width)); + result.push_str(&format!("{:1$x} | ", prev_bitrange.addr, addr_width)); + + let mut contents_str = String::new(); + + let digit_num = accum_bits.len() / digit_bits + if accum_bits.len() % digit_bits == 0 { 0 } else { 1 }; + for digit_index in 0..digit_num + { + if digit_index > 0 && digit_index % byte_digits == 0 + { contents_str.push_str(" "); } + + let mut digit = 0; + for bit_index in 0..digit_bits + { + let i = digit_index * digit_bits + bit_index; + let bit = if i < accum_bits.len() { accum_bits[i] } else { false }; + + digit <<= 1; + digit |= if bit { 1 } else { 0 }; + } + + let c = if digit < 10 + { ('0' as u8 + digit) as char } + else + { ('a' as u8 + digit - 10) as char }; + + contents_str.push(c); + } + + if &*prev_bitrange.span.file != prev_filename + { + prev_filename = &*prev_bitrange.span.file; + prev_file_chars = fileserver.get_chars(RcReport::new(), &prev_filename, None).ok().unwrap(); + } + + let span_location = prev_bitrange.span.location.unwrap(); + let char_counter = CharCounter::new(&prev_file_chars); + + result.push_str(&format!("{:1$}", contents_str, content_width)); + result.push_str(&format!(" ; {}", char_counter.get_excerpt(span_location.0, span_location.1).iter().collect::())); + result.push_str("\n"); + } + + prev_bitrange = bitrange; + accum_bits.clear(); + } + + if bitrange.is_some() + { accum_bits.push(self.read(index)); } + } + + result + } } \ No newline at end of file diff --git a/src/asm/binary_output.rs b/src/asm/binary_output.rs deleted file mode 100644 index 57f85177..00000000 --- a/src/asm/binary_output.rs +++ /dev/null @@ -1,390 +0,0 @@ -pub struct BinaryOutput -{ - bits: Vec -} - - -impl BinaryOutput -{ - pub fn new() -> BinaryOutput - { - BinaryOutput - { - bits: Vec::new() - } - } - - - pub fn write(&mut self, bit_index: usize, bit: bool) - { - while self.bits.len() <= bit_index - { self.bits.push(false); } - - self.bits[bit_index] = bit; - } - - - pub fn read(&self, bit_index: usize) -> bool - { - if bit_index >= self.bits.len() - { false } - else - { self.bits[bit_index] } - } - - - pub fn len(&self) -> usize - { - self.bits.len() - } - - - pub fn generate_binstr(&self, start_bit: usize, end_bit: usize) -> String - { - self.generate_str(1, start_bit, end_bit) - } - - - pub fn generate_hexstr(&self, start_bit: usize, end_bit: usize) -> String - { - self.generate_str(4, start_bit, end_bit) - } - - - pub fn generate_str(&self, digit_bits: usize, start_bit: usize, end_bit: usize) -> String - { - let mut result = String::new(); - - let mut index = start_bit; - while index < end_bit - { - let mut digit: u8 = 0; - for _ in 0..digit_bits - { - digit <<= 1; - digit |= if self.read(index) { 1 } else { 0 }; - index += 1; - } - - let c = if digit < 10 - { ('0' as u8 + digit) as char } - else - { ('a' as u8 + digit - 10) as char }; - - result.push(c); - } - - result - } - - - pub fn generate_bindump(&self, start_bit: usize, end_bit: usize) -> String - { - self.generate_dump(1, start_bit, end_bit, 8, 8) - } - - - pub fn generate_hexdump(&self, start_bit: usize, end_bit: usize) -> String - { - self.generate_dump(4, start_bit, end_bit, 8, 16) - } - - - pub fn generate_dump(&self, digit_bits: usize, start_bit: usize, end_bit: usize, byte_bits: usize, bytes_per_line: usize) -> String - { - let mut result = String::new(); - - let line_start = start_bit / (byte_bits * bytes_per_line); - let line_end = (end_bit + (bytes_per_line - 1) * byte_bits) / (byte_bits * bytes_per_line); - - let line_end = if end_bit - start_bit < byte_bits { line_start + 1 } else { line_end }; - - let addr_max_width = format!("{:x}", (line_end - 1) * bytes_per_line).len(); - - for line_index in line_start..line_end - { - result.push_str(&format!(" {:01$x} | ", line_index * bytes_per_line, addr_max_width)); - - for byte_index in 0..bytes_per_line - { - for digit_index in 0..(byte_bits / digit_bits) - { - let digit_first_bit = (line_index * bytes_per_line + byte_index) * byte_bits + digit_index * digit_bits; - - if digit_first_bit >= self.len() - { - result.push('.'); - continue; - } - - let mut digit = 0; - for bit_index in 0..digit_bits - { - digit <<= 1; - digit |= if self.read(digit_first_bit + bit_index) { 1 } else { 0 }; - } - - let c = if digit < 10 - { ('0' as u8 + digit) as char } - else - { ('a' as u8 + digit - 10) as char }; - - result.push(c); - } - - result.push(' '); - - if byte_index % 4 == 3 && byte_index < bytes_per_line - 1 - { result.push(' '); } - } - - result.push_str("| "); - - if byte_bits == 8 - { - for byte_index in 0..bytes_per_line - { - let byte_first_bit = (line_index * bytes_per_line + byte_index) * byte_bits; - - if byte_first_bit >= self.len() - { - result.push('.'); - continue; - } - - let mut byte = 0u8; - for bit_index in 0..byte_bits - { - byte <<= 1; - byte |= if self.read(byte_first_bit + bit_index) { 1 } else { 0 }; - } - - let c = byte as char; - - if c == ' ' || c == '\t' || c == '\r' || c == '\n' - { result.push(' '); } - else if c as u8 >= 0x80 || c < ' ' || c == '|' - { result.push('.'); } - else - { result.push(c); } - } - - result.push_str(" |"); - } - - result.push('\n'); - } - - result - } - - - pub fn generate_binary(&self, start_bit: usize, end_bit: usize) -> Vec - { - let mut result = Vec::new(); - - let mut index = start_bit; - while index < end_bit - { - let mut byte: u8 = 0; - for _ in 0..8 - { - byte <<= 1; - byte |= if self.read(index) { 1 } else { 0 }; - index += 1; - } - - result.push(byte); - } - - result - } - - - pub fn generate_mif(&self, start_bit: usize, end_bit: usize) -> String - { - let mut result = String::new(); - - let byte_num = (end_bit - start_bit) / 8 + if (end_bit - start_bit) % 8 != 0 { 1 } else { 0 }; - - result.push_str(&format!("DEPTH = {};\n", byte_num)); - result.push_str("WIDTH = 8;\n"); - result.push_str("ADDRESS_RADIX = HEX;\n"); - result.push_str("DATA_RADIX = HEX;\n"); - result.push_str("\n"); - result.push_str("CONTENT\n"); - result.push_str("BEGIN\n"); - - let addr_max_width = format!("{:x}", byte_num - 1).len(); - - let mut index = start_bit; - while index < end_bit - { - result.push_str(&format!(" {:1$X}: ", index / 8, addr_max_width)); - - let mut byte: u8 = 0; - for _ in 0..8 - { - byte <<= 1; - byte |= if self.read(index) { 1 } else { 0 }; - index += 1; - } - - result.push_str(&format!("{:02X};\n", byte)); - } - - result.push_str("END;"); - result - } - - - pub fn generate_intelhex(&self, start_bit: usize, end_bit: usize) -> String - { - let mut result = String::new(); - - let mut bytes_left = (end_bit - start_bit) / 8 + if (end_bit - start_bit) % 8 != 0 { 1 } else { 0 }; - - let mut index = start_bit; - while index < end_bit - { - let bytes_in_row = if bytes_left > 32 { 32 } else { bytes_left }; - - result.push(':'); - result.push_str(&format!("{:02X}", bytes_in_row)); - result.push_str(&format!("{:04X}", index / 8)); - result.push_str("00"); - - let mut checksum = 0_u8; - checksum = checksum.wrapping_add(bytes_in_row as u8); - checksum = checksum.wrapping_add(((index / 8) >> 8) as u8); - checksum = checksum.wrapping_add((index / 8) as u8); - - for _ in 0..bytes_in_row - { - let mut byte: u8 = 0; - for _ in 0..8 - { - byte <<= 1; - byte |= if self.read(index) { 1 } else { 0 }; - index += 1; - } - - result.push_str(&format!("{:02X}", byte)); - checksum = checksum.wrapping_add(byte); - } - - bytes_left -= bytes_in_row; - result.push_str(&format!("{:02X}", (!checksum).wrapping_add(1))); - result.push('\n'); - } - - result.push_str(":00000001FF"); - result - } - - - pub fn generate_comma(&self, start_bit: usize, end_bit: usize, radix: usize) -> String - { - let mut result = String::new(); - - let mut index = start_bit; - while index < end_bit - { - let mut byte: u8 = 0; - for _ in 0..8 - { - byte <<= 1; - byte |= if self.read(index) { 1 } else { 0 }; - index += 1; - } - - match radix - { - 10 => result.push_str(&format!("{}", byte)), - 16 => result.push_str(&format!("0x{:02x}", byte)), - _ => panic!("invalid radix") - } - - if index < end_bit - { - result.push_str(", "); - - if (index / 8) % 16 == 0 - { result.push('\n'); } - } - } - - result - } - - - pub fn generate_c_array(&self, start_bit: usize, end_bit: usize, radix: usize) -> String - { - let mut result = String::new(); - - result.push_str("const unsigned char data[] = {\n"); - - let byte_num = (end_bit - start_bit) / 8 + if (end_bit - start_bit) % 8 != 0 { 1 } else { 0 }; - let addr_max_width = format!("{:x}", byte_num - 1).len(); - - let mut index = start_bit; - result.push_str(&format!("\t/* 0x{:01$x} */ ", 0, addr_max_width)); - - while index < end_bit - { - let mut byte: u8 = 0; - for _ in 0..8 - { - byte <<= 1; - byte |= if self.read(index) { 1 } else { 0 }; - index += 1; - } - - match radix - { - 10 => result.push_str(&format!("{}", byte)), - 16 => result.push_str(&format!("0x{:02x}", byte)), - _ => panic!("invalid radix") - } - - if index < end_bit - { - result.push_str(", "); - - if (index / 8) % 16 == 0 - { - result.push_str(&format!("\n\t/* 0x{:01$x} */ ", index / 8, addr_max_width)); - } - } - } - - result.push_str("\n};"); - result - } - - - // From: https://github.com/milanvidakovic/customasm/blob/master/src/asm/binary_output.rs#L84 - pub fn generate_logisim(&self, start_bit: usize, end_bit: usize, bits_per_chunk: usize) -> String - { - let mut result = String::new(); - result.push_str("v2.0 raw\n"); - - let mut index = start_bit; - while index < end_bit - { - let mut value: u16 = 0; - for _ in 0..bits_per_chunk - { - value <<= 1; - value |= if self.read(index) { 1 } else { 0 }; - index += 1; - } - - result.push_str(&format!("{:01$x} ", value, bits_per_chunk / 4)); - if (index / 8) % 16 == 0 - { result.push('\n'); } - } - - result - } -} \ No newline at end of file diff --git a/src/asm/mod.rs b/src/asm/mod.rs index 244f32e4..91f67c85 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -1,6 +1,6 @@ mod assembler; mod parser; -mod binary_output; +mod bank; mod label; mod bankdef; mod binary_block; @@ -13,9 +13,10 @@ pub use self::assembler::AssemblerState; pub use self::assembler::ParsedInstruction; pub use self::assembler::ParsedExpression; pub use self::assembler::ExpressionContext; +pub use self::assembler::BitRangeSpan; pub use self::parser::AssemblerParser; -pub use self::binary_output::BinaryOutput; pub use self::label::LabelManager; pub use self::label::LabelContext; pub use self::bankdef::BankDef; +pub use self::bank::Bank; pub use self::binary_block::BinaryBlock; \ No newline at end of file diff --git a/src/asm/parser.rs b/src/asm/parser.rs index 4b8f2dbf..b0a5fdb2 100644 --- a/src/asm/parser.rs +++ b/src/asm/parser.rs @@ -5,7 +5,7 @@ use crate::expr::{Expression, ExpressionValue, ExpressionEvalContext}; use crate::asm::{AssemblerState, ParsedInstruction, ParsedExpression}; use crate::asm::cpudef::CpuDef; use crate::asm::BankDef; -use crate::asm::BinaryBlock; +use crate::asm::Bank; use crate::util::filename_navigate; use crate::util::FileServer; use num_bigint::BigInt; @@ -83,7 +83,7 @@ impl<'a> AssemblerParser<'a> fn parse_directive(&mut self) -> Result<(), ()> { - self.parser.expect(TokenKind::Hash)?; + let tk_hash = self.parser.expect(TokenKind::Hash)?; let tk_name = self.parser.expect(TokenKind::Identifier)?; let name = tk_name.excerpt.clone().unwrap(); @@ -91,7 +91,7 @@ impl<'a> AssemblerParser<'a> if name.chars().next() == Some('d') { if let Ok(elem_width) = usize::from_str_radix(&name[1..], 10) - { return self.parse_directive_data(elem_width, &tk_name); } + { return self.parse_directive_data(elem_width, &tk_hash, &tk_name); } } match name.as_ref() @@ -140,7 +140,7 @@ impl<'a> AssemblerParser<'a> if self.state.find_bankdef(&bankname).is_some() { return Err(self.parser.report.error_span("duplicate bank name", &tk_bankname.span)); } - if self.state.blocks[0].len() > 0 + if self.state.blocks[0].bits.len() > 0 { return Err(self.parser.report.error_span("cannot define bank after using the default bank", &tk_bankname.span)); } self.parser.expect(TokenKind::BraceOpen)?; @@ -148,7 +148,7 @@ impl<'a> AssemblerParser<'a> self.parser.expect(TokenKind::BraceClose)?; self.state.bankdefs.push(bankdef); - self.state.blocks.push(BinaryBlock::new(bankname)); + self.state.blocks.push(Bank::new(bankname)); self.state.cur_bank = self.state.bankdefs.len() - 1; self.state.cur_block = self.state.bankdefs.len() - 1; Ok(()) @@ -191,13 +191,13 @@ impl<'a> AssemblerParser<'a> if bankdef.outp.is_some() { return Err(self.parser.report.error_span("cannot seek to previous address", &tk_name.span)); } - self.state.blocks[self.state.cur_block].truncate(new_addr * bits); + self.state.blocks[self.state.cur_block].bits.truncate(new_addr * bits); Ok(()) } else { let bits_to_skip = (new_addr - cur_addr) * bits; - self.state.output_zero_bits(self.parser.report.clone(), bits_to_skip, true, &tk_name.span) + self.state.output_zero_bits(self.parser.report.clone(), bits_to_skip, true, &tk_name.span, None) } } @@ -208,7 +208,7 @@ impl<'a> AssemblerParser<'a> let addr_multiple_of = self.parse_usize()?; - self.state.output_bits_until_aligned(self.parser.report.clone(), addr_multiple_of, &tk_name.span) + self.state.output_bits_until_aligned(self.parser.report.clone(), addr_multiple_of, &tk_name.span, None) } @@ -218,7 +218,7 @@ impl<'a> AssemblerParser<'a> let bits = self.parse_usize()? * self.state.cpudef.as_ref().unwrap().bits; - self.state.output_zero_bits(self.parser.report.clone(), bits, true, &tk_name.span) + self.state.output_zero_bits(self.parser.report.clone(), bits, true, &tk_name.span, Some(&tk_name.span)) } @@ -232,7 +232,7 @@ impl<'a> AssemblerParser<'a> for _ in 0..8 { let bit = byte & 0x80 != 0; - self.state.output_bit(self.parser.report.clone(), bit, false, &tk_string.span)?; + self.state.output_bit(self.parser.report.clone(), bit, false, &tk_string.span, Some(&tk_string.span))?; byte <<= 1; } } @@ -266,7 +266,7 @@ impl<'a> AssemblerParser<'a> for _ in 0..8 { let bit = byte & 0x80 != 0; - self.state.output_bit(self.parser.report.clone(), bit, false, &tk_filename.span)?; + self.state.output_bit(self.parser.report.clone(), bit, false, &tk_filename.span, Some(&tk_filename.span))?; byte <<= 1; } } @@ -295,7 +295,7 @@ impl<'a> AssemblerParser<'a> for _ in 0..bits_per_char { let bit = digit & (1 << (bits_per_char - 1)) != 0; - self.state.output_bit(self.parser.report.clone(), bit, false, &tk_filename.span)?; + self.state.output_bit(self.parser.report.clone(), bit, false, &tk_filename.span, Some(&tk_filename.span))?; digit <<= 1; } } @@ -304,16 +304,22 @@ impl<'a> AssemblerParser<'a> } - fn parse_directive_data(&mut self, elem_width: usize, tk_name: &Token) -> Result<(), ()> + fn parse_directive_data(&mut self, elem_width: usize, tk_hash: &Token, tk_name: &Token) -> Result<(), ()> { if elem_width == 0 { return Err(self.parser.report.error_span("invalid element width", &tk_name.span)); } + let mut is_first = true; + loop { let ctx = self.state.get_cur_context(); let expr = Expression::parse(&mut self.parser)?; let span = expr.span(); + let output_span = if is_first + { expr.span().join(&tk_hash.span) } + else + { expr.span() }; let parsed_expr = ParsedExpression { @@ -324,10 +330,12 @@ impl<'a> AssemblerParser<'a> self.state.parsed_exprs.push(parsed_expr); - self.state.output_zero_bits(self.parser.report.clone(), elem_width, false, &span)?; + self.state.output_zero_bits(self.parser.report.clone(), elem_width, false, &span, Some(&output_span))?; if self.parser.maybe_expect(TokenKind::Comma).is_none() { break; } + + is_first = false; } Ok(()) @@ -336,7 +344,8 @@ impl<'a> AssemblerParser<'a> fn parse_label(&mut self) -> Result<(), ()> { - let is_local = self.parser.maybe_expect(TokenKind::Dot).is_some(); + let tk_dot = self.parser.maybe_expect(TokenKind::Dot); + let is_local = tk_dot.is_some(); let mut name = if is_local { "." } else { "" }.to_string(); let tk_name = self.parser.expect(TokenKind::Identifier)?; @@ -353,15 +362,20 @@ impl<'a> AssemblerParser<'a> } else { - self.parser.expect(TokenKind::Colon)?; + let tk_colon = self.parser.expect(TokenKind::Colon)?; + + let mut span_full = tk_name.span.join(&tk_colon.span); + if let Some(tk_dot) = tk_dot + { span_full = span_full.join(&tk_dot.span); } - self.state.check_cpudef_active(self.parser.report.clone(), &tk_name.span)?; + self.state.check_cpudef_active(self.parser.report.clone(), &span_full)?; let label_align = self.state.cpudef.as_ref().unwrap().label_align; if label_align.is_some() - { self.state.output_bits_until_aligned(self.parser.report.clone(), label_align.unwrap(), &tk_name.span)?; } + { self.state.output_bits_until_aligned(self.parser.report.clone(), label_align.unwrap(), &span_full, None)?; } - let addr = self.state.get_cur_address(self.parser.report.clone(), &tk_name.span)?; + let addr = self.state.get_cur_address(self.parser.report.clone(), &span_full)?; + self.state.mark_label(addr, &span_full); ExpressionValue::Integer(BigInt::from(addr)) }; @@ -442,7 +456,7 @@ impl<'a> AssemblerParser<'a> rule.production.width().unwrap() }; - self.state.output_zero_bits(self.parser.report.clone(), instr_width, false, &instr_span) + self.state.output_zero_bits(self.parser.report.clone(), instr_width, false, &instr_span, Some(&instr_span)) } // ...or if all arguments could be resolved, output instruction now. diff --git a/src/diagn/span.rs b/src/diagn/span.rs index 0278b732..8b64f850 100644 --- a/src/diagn/span.rs +++ b/src/diagn/span.rs @@ -93,4 +93,13 @@ impl Span location: location } } +} + + +impl PartialEq for Span +{ + fn eq(&self, other: &Self) -> bool + { + self.file == other.file && self.location == other.location + } } \ No newline at end of file diff --git a/src/driver.rs b/src/driver.rs index d2ba693e..caa73b75 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,7 +1,7 @@ use crate::diagn::RcReport; use crate::util::FileServer; use crate::util::enable_windows_ansi_support; -use crate::asm::BinaryOutput; +use crate::asm::BinaryBlock; use crate::asm::AssemblerState; use std::io::stdout; use getopts; @@ -10,6 +10,8 @@ use getopts; enum OutputFormat { Binary, + AnnotatedHex, + AnnotatedBin, BinStr, HexStr, BinDump, @@ -70,6 +72,10 @@ fn drive_inner(report: RcReport, opts: &getopts::Options, args: &Vec, fi let out_format = match matches.opt_str("f").as_ref().map(|s| s.as_ref()) { + Some("annotated") => OutputFormat::AnnotatedHex, + Some("annotatedhex") => OutputFormat::AnnotatedHex, + Some("annotatedbin") => OutputFormat::AnnotatedBin, + Some("binstr") => OutputFormat::BinStr, Some("bindump") => OutputFormat::BinDump, Some("hexstr") => OutputFormat::HexStr, @@ -86,7 +92,7 @@ fn drive_inner(report: RcReport, opts: &getopts::Options, args: &Vec, fi Some("logisim16") => OutputFormat::LogiSim16, None => if out_stdout - { OutputFormat::HexDump } + { OutputFormat::AnnotatedHex } else { OutputFormat::Binary }, @@ -123,7 +129,8 @@ fn drive_inner(report: RcReport, opts: &getopts::Options, args: &Vec, fi let output_data = match out_format { - OutputFormat::Binary => assembled.generate_binary (0, assembled.len()), + OutputFormat::Binary => assembled.generate_binary(0, assembled.len()), + OutputFormat::BinStr => assembled.generate_binstr (0, assembled.len()).bytes().collect::>(), OutputFormat::BinDump => assembled.generate_bindump (0, assembled.len()).bytes().collect::>(), OutputFormat::HexStr => assembled.generate_hexstr (0, assembled.len()).bytes().collect::>(), @@ -136,6 +143,9 @@ fn drive_inner(report: RcReport, opts: &getopts::Options, args: &Vec, fi OutputFormat::HexC => assembled.generate_c_array (0, assembled.len(), 16).bytes().collect::>(), OutputFormat::LogiSim8 => assembled.generate_logisim (0, assembled.len(), 8).bytes().collect::>(), OutputFormat::LogiSim16 => assembled.generate_logisim (0, assembled.len(), 16).bytes().collect::>(), + + OutputFormat::AnnotatedHex => assembled.generate_annotated_hex(fileserver, 0, assembled.len()).bytes().collect::>(), + OutputFormat::AnnotatedBin => assembled.generate_annotated_bin(fileserver, 0, assembled.len()).bytes().collect::>(), }; if out_stdout @@ -164,7 +174,7 @@ fn drive_inner(report: RcReport, opts: &getopts::Options, args: &Vec, fi fn make_opts() -> getopts::Options { let mut opts = getopts::Options::new(); - opts.optopt("f", "format", "The format of the output file. Possible formats: binary, binstr, hexstr, bindump, hexdump, mif, intelhex, deccomma, hexcomma, decc, hexc, logisim8, logisim16", "FORMAT"); + opts.optopt("f", "format", "The format of the output file. Possible formats: binary, annotated, annotatedbin, binstr, hexstr, bindump, hexdump, mif, intelhex, deccomma, hexcomma, decc, hexc, logisim8, logisim16", "FORMAT"); opts.optmulti("i", "include", "Specifies an additional file for processing before the given . [deprecated]", "FILE"); opts.optopt("o", "output", "The name of the output file.", "FILE"); opts.optflag("p", "print", "Print output to stdout instead of writing to a file."); @@ -220,7 +230,7 @@ fn get_default_output_filename(report: RcReport, input_filename: &str) -> Result } -pub fn assemble(report: RcReport, fileserver: &dyn FileServer, filenames: &[String], quiet: bool) -> Result +pub fn assemble(report: RcReport, fileserver: &dyn FileServer, filenames: &[String], quiet: bool) -> Result { if !quiet { print_header(); } diff --git a/src/util/fileserver.rs b/src/util/fileserver.rs index f95c56a3..3cd57523 100644 --- a/src/util/fileserver.rs +++ b/src/util/fileserver.rs @@ -1,4 +1,5 @@ use crate::diagn::{Span, RcReport}; +use crate::util::CharCounter; use std::collections::HashMap; use std::fs::File; use std::io::{Read, Write}; @@ -19,6 +20,15 @@ pub trait FileServer fn write_bytes(&mut self, report: RcReport, filename: &str, data: &Vec, span: Option<&Span>) -> Result<(), ()>; + + + fn get_excerpt(&self, span: &Span) -> String + { + let chars = self.get_chars(RcReport::new(), &*span.file, None).ok().unwrap(); + let counter = CharCounter::new(&chars); + let location = span.location.unwrap(); + counter.get_excerpt(location.0, location.1).iter().collect() + } } diff --git a/src/webasm/mod.rs b/src/webasm/mod.rs index 97353eba..c97c15a1 100644 --- a/src/webasm/mod.rs +++ b/src/webasm/mod.rs @@ -22,18 +22,21 @@ pub unsafe extern fn wasm_assemble(format: u32, src: *mut String) -> *mut String let output = asm.get_binary_output(); match format { - 0 => Ok(output.generate_hexdump (0, output.len())), - 1 => Ok(output.generate_bindump (0, output.len())), - 2 => Ok(output.generate_hexstr (0, output.len())), - 3 => Ok(output.generate_binstr (0, output.len())), - 4 => Ok(output.generate_mif (0, output.len())), - 5 => Ok(output.generate_intelhex(0, output.len())), - 6 => Ok(output.generate_comma (0, output.len(), 10)), - 7 => Ok(output.generate_comma (0, output.len(), 16)), - 8 => Ok(output.generate_c_array (0, output.len(), 10)), - 9 => Ok(output.generate_c_array (0, output.len(), 16)), - 10 => Ok(output.generate_logisim (0, output.len(), 8)), - 11 => Ok(output.generate_logisim (0, output.len(), 16)), + 0 => Ok(output.generate_annotated_hex(fileserver, 0, output.len())), + 1 => Ok(output.generate_annotated_bin(fileserver, 0, output.len())), + + 2 => Ok(output.generate_hexdump (0, output.len())), + 3 => Ok(output.generate_bindump (0, output.len())), + 4 => Ok(output.generate_hexstr (0, output.len())), + 5 => Ok(output.generate_binstr (0, output.len())), + 6 => Ok(output.generate_mif (0, output.len())), + 7 => Ok(output.generate_intelhex(0, output.len())), + 8 => Ok(output.generate_comma (0, output.len(), 10)), + 9 => Ok(output.generate_comma (0, output.len(), 16)), + 10 => Ok(output.generate_c_array (0, output.len(), 10)), + 11 => Ok(output.generate_c_array (0, output.len(), 16)), + 12 => Ok(output.generate_logisim (0, output.len(), 8)), + 13 => Ok(output.generate_logisim (0, output.len(), 16)), _ => unreachable!() } }; diff --git a/web/customasm.gc.wasm b/web/customasm.gc.wasm index a9313ba4..05d93189 100644 Binary files a/web/customasm.gc.wasm and b/web/customasm.gc.wasm differ diff --git a/web/index.html b/web/index.html index 797a35f9..bd0a44cf 100644 --- a/web/index.html +++ b/web/index.html @@ -90,6 +90,8 @@
Output Format: