trion/asm/directive/
align.rs1use core::fmt;
2use std::error::Error;
3
4use crate::asm::{ConstantError, Context, ErrorLevel, SegmentError};
5use crate::asm::constant::Realm;
6use crate::asm::directive::{Directive, DirectiveErrorKind};
7use crate::asm::simplify::{evaluate, Evaluation};
8use crate::text::Positioned;
9use crate::text::parse::{Argument, ArgumentType};
10use crate::text::parse::choice::ArgChoice;
11use crate::text::token::Number;
12
13#[derive(Clone, Copy, Debug)]
14pub struct Align;
15
16impl Directive for Align
17{
18 fn get_name(&self) -> &str
19 {
20 "align"
21 }
22
23 fn apply(&self, ctx: & mut Context, mut args: Positioned<Vec<Argument>>) -> Result<(), ErrorLevel>
24 {
25 if ctx.active().is_none()
26 {
27 let source = Box::new(AlignError::Inactive);
28 ctx.push_error(args.convert(DirectiveErrorKind::Apply{dir: self.get_name().to_owned(), source}));
29 return Err(ErrorLevel::Fatal);
30 }
31 const NUM_ARGS: usize = 1;
32 if args.value.len() != NUM_ARGS
33 {
34 let dir = self.get_name().to_owned();
35 ctx.push_error(args.convert(
36 if args.value.len() < NUM_ARGS {DirectiveErrorKind::NotEnoughArguments{dir, need: NUM_ARGS, have: args.value.len()}}
37 else {DirectiveErrorKind::TooManyArguments{dir, max: NUM_ARGS, have: args.value.len()}}
38 ));
39 return Err(ErrorLevel::Trivial);
40 }
41 match evaluate(&mut args.value[0], ctx)
42 {
43 Ok(Evaluation::Complete{..}) => (),
44 Ok(Evaluation::Deferred{cause, ..}) =>
45 {
46 let source = Box::new(ConstantError::NotFound{name: cause.as_ref().to_owned(), realm: Realm::Local});
47 ctx.push_error(args.convert(DirectiveErrorKind::Apply{dir: self.get_name().to_owned(), source}));
48 return Err(ErrorLevel::Fatal);
49 },
50 Err(e) =>
51 {
52 ctx.push_error(args.convert(DirectiveErrorKind::Apply{dir: self.get_name().to_owned(), source: Box::new(e)}));
53 return Err(ErrorLevel::Fatal);
54 },
55 }
56 let active = ctx.active_mut().unwrap();
57 let len = match args.value[0]
58 {
59 Argument::Constant(ref num) =>
60 {
61 let &Number::Integer(val) = num;
62 match u32::try_from(val)
63 {
64 Ok(val) if val > 0 => val,
65 _ =>
66 {
67 let source = Box::new(AlignError::Range(val));
68 ctx.push_error(args.convert(DirectiveErrorKind::Apply{dir: self.get_name().to_owned(), source}));
69 return Err(ErrorLevel::Fatal);
70 },
71 }
72 },
73 ref arg =>
74 {
75 let dir = self.get_name().to_owned();
76 ctx.push_error(args.convert(DirectiveErrorKind::ArgumentType
77 {
78 dir,
79 idx: 0,
80 expect: ArgChoice::of(ArgumentType::Constant),
81 have: arg.get_type(),
82 }));
83 return Err(ErrorLevel::Trivial);
84 },
85 };
86 let off = active.curr_addr() % len;
87 if off != 0
88 {
89 match usize::try_from(len - off)
90 {
91 Ok(new_len) =>
92 {
93 const PADDING: [u8; 256] = [0xBE; 256];
94 if !active.has_remaining(new_len)
95 {
96 let source = Box::new(AlignError::Write(SegmentError::Overflow{need: new_len, have: active.remaining()}));
97 ctx.push_error(args.convert(DirectiveErrorKind::Apply{dir: self.get_name().to_owned(), source}));
98 return Err(ErrorLevel::Fatal);
99 }
100 for p in (0..new_len).step_by(256)
101 {
102 let result = if new_len - p >= 256 {active.write(&PADDING)}
103 else {active.write(&PADDING[..new_len - p])};
104 if let Err(e) = result
105 {
106 let source = Box::new(AlignError::Write(e));
107 ctx.push_error(args.convert(DirectiveErrorKind::Apply{dir: self.get_name().to_owned(), source}));
108 return Err(ErrorLevel::Fatal);
109 }
110 }
111 },
112 Err(..) =>
113 {
114 let source = Box::new(AlignError::Overflow{need: len - off, have: active.remaining()});
115 ctx.push_error(args.convert(DirectiveErrorKind::Apply{dir: self.get_name().to_owned(), source}));
116 return Err(ErrorLevel::Fatal);
117 },
118 }
119 }
120 Ok(())
121 }
122}
123
124#[derive(Debug)]
125pub enum AlignError
126{
127 Inactive,
128 Range(i64),
129 Overflow{need: u32, have: usize},
130 Write(SegmentError),
131}
132
133impl fmt::Display for AlignError
134{
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
136 {
137 match self
138 {
139 Self::Inactive => f.write_str("no active segment to align"),
140 Self::Range(align) => write!(f, "alignment out of range ({align})"),
141 Self::Overflow{need, have} => write!(f, "alignment too long (need {need}, max {have})"),
142 Self::Write(..) => f.write_str("could not write alignment bytes"),
143 }
144 }
145}
146
147impl Error for AlignError
148{
149 fn source(&self) -> Option<&(dyn Error + 'static)>
150 {
151 match self
152 {
153 Self::Write(e) => Some(e),
154 _ => None,
155 }
156 }
157}