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