Skip to content

Commit

Permalink
fix #15: add annotated output format
Browse files Browse the repository at this point in the history
  • Loading branch information
hlorenzi committed Aug 20, 2019
1 parent eaf702f commit 1557a05
Show file tree
Hide file tree
Showing 15 changed files with 793 additions and 489 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "customasm"
version = "0.10.0"
version = "0.10.1"
edition = "2018"
authors = ["Henrique Lorenzi <[email protected]>"]

Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ Usage: customasm [options] <asm-file-1> ... <asm-file-N>
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.
Expand Down Expand Up @@ -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
```
2 changes: 1 addition & 1 deletion examples/nes/ines_nrom.asm
Original file line number Diff line number Diff line change
@@ -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 }

Expand Down
130 changes: 102 additions & 28 deletions src/asm/assembler.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,7 +17,7 @@ pub struct AssemblerState
pub parsed_exprs: Vec<ParsedExpression>,

pub bankdefs: Vec<BankDef>,
pub blocks: Vec<BinaryBlock>,
pub blocks: Vec<Bank>,
pub cur_bank: usize,
pub cur_block: usize
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -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(()),
Expand All @@ -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
{
Expand All @@ -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); }
}
}
}
Expand Down Expand Up @@ -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()
}
}
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -220,6 +253,28 @@ impl AssemblerState
}


fn get_bitrange_address(&self, report: RcReport, span: &Span) -> Result<usize, ()>
{
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];
Expand All @@ -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<(), ()>
Expand Down Expand Up @@ -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(())
Expand All @@ -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(())
Expand Down Expand Up @@ -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();
Expand Down
22 changes: 22 additions & 0 deletions src/asm/bank.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::asm::BinaryBlock;


pub struct Bank
{
pub bank_name: String,
pub bits: BinaryBlock
}


impl Bank
{
pub fn new<S>(bank_name: S) -> Bank
where S: Into<String>
{
Bank
{
bank_name: bank_name.into(),
bits: BinaryBlock::new()
}
}
}
Loading

0 comments on commit 1557a05

Please sign in to comment.