1use std::collections::BTreeMap;
2use std::collections::HashMap;
3use std::error::Error;
4use std::fs::File;
5use std::io;
6use std::io::prelude::*;
7use std::io::BufRead;
8use std::u64;
9use crate::number_parser;
10mod parser;
11use parser::ICode;
12
13pub struct Y86Assembler {
16 bytes: Vec<u8>,
17}
18
19impl Y86Assembler {
20 pub fn from_file(file_name: String) -> Result<Self, Box<dyn Error>> {
24 let lines_iter = read_lines(file_name)?;
25 let lines: Vec<String> = lines_iter.map(|val| val.unwrap()).collect();
26 let mut positions: BTreeMap<u64, Vec<u8>> = BTreeMap::new();
27 get_positions(&mut positions, &lines)?;
28 Ok(Y86Assembler {
29 bytes: merge_position(&positions),
30 })
31 }
32
33 pub fn save_file(&mut self, file_name: String) -> Result<(), Box<dyn Error>> {
36 let mut file = File::create(file_name)?;
37 file.write_all(&self.bytes)?;
38 Ok(())
39 }
40
41 pub fn print(&self) {
43 self.bytes.iter().for_each(|b| print!("{:02x}", b));
44 println!();
45 }
46}
47
48fn merge_position(positions: &BTreeMap<u64, Vec<u8>>) -> Vec<u8> {
49 let iter = positions.iter();
50 let mut res = vec![];
51 for (key, val) in iter {
52 while res.len() < *key as usize {
53 res.push(0);
54 }
55 val.iter().for_each(|&b| res.push(b));
56 }
57 res
58}
59
60fn get_positions(
61 positions: &mut BTreeMap<u64, Vec<u8>>,
62 lines: &[String],
63) -> Result<(), Box<dyn Error>> {
64 let mut curr_position = 0;
65 let trimmed: Vec<String> = lines.iter().map(|line| trim_line(&line)).collect();
66 let mapping: HashMap<&str, u64> = map_labels(&trimmed)?;
67 let val: Result<(), Box<dyn Error>> = trimmed
68 .iter()
69 .map(|line| apply_mapping(&mapping, &line))
70 .try_for_each(|line| {
71 if line.starts_with(".pos") {
72 let position: u64 = number_parser::parse_num(&line[5..])?;
73 positions.insert(position, vec![]);
74 curr_position = position;
75 } else {
76 let curr_vec = positions.entry(curr_position).or_insert_with(|| vec![]);
77 curr_vec.append(&mut convert_line(&line)?);
78 }
79 Ok(())
80 });
81 val
82}
83
84fn trim_line(line: &str) -> String {
85 let mut res = line.trim().to_string();
86 if res.contains('#') {
87 res = res[..res.find('#').unwrap()].to_string();
88 }
89 res.replace("$", "")
90}
91
92fn apply_mapping(mapping: &HashMap<&str, u64>, line: &str) -> String {
93 let mut res = String::new();
94 if line.contains(':') {
95 res.push_str(line[line.find(':').unwrap() + 1..].trim());
96 } else {
97 res = line.to_string();
98 }
99 let mut v: Vec<(&str, u64)> = Vec::new();
100 mapping.iter().for_each(|(key, &val)| v.push((key, val)));
101 v.sort_by(|a, b| {
102 let (key, _val) = a;
103 let (key_2, _val_2) = b;
104 key_2.len().partial_cmp(&key.len()).unwrap()
105 });
106 v.iter().for_each(|(key, val)| {
107 let mut expected = " ".to_string();
108 expected.push_str(key);
109 if res.contains(&expected) {
110 res = res.replace(key, &format!("0x{:x}", val));
111 }
112 });
113 res
114}
115
116fn instr_size(line: &str) -> Result<u64, Box<dyn Error>> {
117 let mut split = line.split(' ');
118 let instr = split.next().unwrap();
119 let val = match parser::get_icode_from_string(instr)? {
120 ICode::IIRMOVQ | ICode::IRMMOVQ | ICode::IMRMOVQ => 10,
121 ICode::IRRMVXX | ICode::IOPQ | ICode::IPOPQ | ICode::IPUSHQ => 2,
122 ICode::IJXX | ICode::ICALL => 9,
123 ICode::IHALT | ICode::INOP | ICode::IRET => 1,
124 _ => 0,
125 };
126 Ok(val)
127}
128
129fn map_labels(lines: &[String]) -> Result<HashMap<&str, u64>, Box<dyn Error>> {
130 let mut res: HashMap<&str, u64> = HashMap::new();
131 let mut curr_addr = 0;
132 let val: Result<(), Box<dyn Error>> = lines.iter().try_for_each(|line| {
133 if line.starts_with(".pos") {
134 let position: u64 = number_parser::parse_num(&line[5..])?;
135 curr_addr = position;
136 } else {
137 if line.contains(':') {
138 let mut split = line.split(':');
139 res.insert(split.next().unwrap().trim(), curr_addr);
140 }
141 if line.contains(".quad") {
142 curr_addr += 8;
143 } else if !line.is_empty() {
144 let mut line = line.clone();
145 if line.contains(':') {
146 line = line[line.find(':').unwrap() + 1..].trim().to_string();
147 }
148 curr_addr += instr_size(&line)?;
149 }
150 }
151 Ok(())
152 });
153 val?;
154 Ok(res)
155}
156
157fn convert_line(line: &str) -> Result<Vec<u8>, Box<dyn Error>> {
158 if line.trim().is_empty() {
159 return Ok(vec![]);
160 }
161 parser::parse(line)
162}
163
164fn read_lines(file_name: String) -> io::Result<io::Lines<io::BufReader<File>>> {
165 let file = File::open(file_name)?;
166 Ok(io::BufReader::new(file).lines())
167}
168
169