trion/asm/directive/
align.rs

1use 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}