y86_lib/
assembler.rs

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
13/// A struct to hold bytes read from y86
14/// bytes is a vector holding the bytes
15pub struct Y86Assembler {
16    bytes: Vec<u8>,
17}
18
19impl Y86Assembler {
20    /// file_name: a string holding the file name to read
21    /// reads a Y86 file and generates a Y86Assembler with the
22    /// machine code content
23    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    /// Saves a the machine code content into a file specified by 
34    /// file_name
35    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    /// Prints the machine code content onto stdout
42    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// Go over each .pos, starting form there, pump values into a hashmap
170// Sort the map by key, then add values, with 000 between to the end result.