ch8_isa/codegen/
binary.rs

1/*
2 * binary.rs
3 * Defines a struct that represents a Chip-8 binary
4 * Created on 12/6/2019
5 * Created by Andrew Davis
6 *
7 * Copyright (C) 2019  Andrew Davis
8 *
9 * This program is free software: you can redistribute it and/or modify   
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 * 
19 * You should have received a copy of the GNU General Public License
20 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23//crate import
24extern crate byteorder;
25
26//usage statements
27use byteorder::WriteBytesExt;
28use byteorder::BigEndian;
29use super::Instruction;
30use super::CodeGen;
31use std::fs::File;
32use std::path::Path;
33use std::io::prelude::*;
34use super::super::error::{BinaryError, BinaryErrorType};
35
36/// A Chip-8 binary
37pub struct Binary {
38    /// The raw bytes that make up
39    /// the binary
40    data: Vec<u8>,
41
42    /// The path to the binary file
43    name: String,
44
45    /// The length of the binary (in bytes)
46    length: u16 
47}
48
49//implementation
50impl Binary {
51    /// Constructs a new `Binary` instance
52    ///
53    /// # Argument
54    ///
55    /// * `new_name` - The name of the binary file 
56    ///
57    /// # Returns
58    ///
59    /// A new `Binary` instance, wrapped in a `Result`
60    pub fn new(new_name: &str) -> Result<Binary, BinaryError> {
61        //check to see if the name exists
62        if Path::new(new_name).exists() {
63            return Err(BinaryError::new(BinaryErrorType::FileExists,
64                                        new_name));
65        } else {
66            return Ok(Binary {
67                data: Vec::new(),
68                name: String::from(new_name),
69                length: 0
70            });
71        }
72    }
73
74    /// Gets the length of the binary
75    /// 
76    /// # Returns
77    ///
78    /// The length of the binary (in bytes)
79    pub fn len(&self) -> u16 {
80        return self.length;
81    }
82
83    /// Adds an instruction to the binary
84    /// 
85    /// # Argument
86    ///
87    /// * `instr` - The `Instruction` to add
88    ///
89    /// # Returns
90    ///
91    /// `Ok` if the instruction was added successfully,
92    /// `Err<BinaryError>` if the operation fails
93    pub fn add_instruction(&mut self, instr: &Instruction)
94        -> Result<(), BinaryError> {
95        //add the instruction and return the result
96        return self.add_word(instr.gen_opcode());
97    }
98
99    /// Adds a 16-bit word to the binary 
100    /// 
101    /// # Argument
102    ///
103    /// * `word` - The word to add
104    ///
105    /// # Returns
106    ///
107    /// `Ok` if the word was written successfully,
108    /// `Err<BinaryError>` if the operation fails
109    pub fn add_word(&mut self, word: u16) -> Result<(), BinaryError> {
110        //attempt to add the word
111        let res = self.data.write_u16::<BigEndian>(word);
112
113        //and determine whether an error occurred
114        if res.is_err() {
115            return Err(BinaryError::new(BinaryErrorType::DataError,
116                                        self.name.as_str()));
117        } else {
118            self.length += 2;
119            return Ok(());
120        }
121    }
122
123    /// Adds a byte to the binary
124    /// 
125    /// # Argument
126    ///
127    /// * `byte` - The byte to add
128    /// 
129    /// # Returns
130    ///
131    /// `Ok` if the byte was written successfully,
132    /// `Err<BinaryError>` if the operation fails
133    pub fn add_byte(&mut self, byte: u8) -> Result<(), BinaryError> {
134        //attempt to write the byte
135        let res = self.data.write_u8(byte);
136
137        //and determine whether an error occurred
138        if res.is_err() {
139            return Err(BinaryError::new(BinaryErrorType::DataError,
140                                        self.name.as_str()));
141        } else {
142            self.length += 1;
143            return Ok(());
144        }
145    }
146
147    /// Writes the entire binary to a file
148    /// 
149    /// # Returns
150    ///
151    /// A `Result` that on a success contains the number of bytes
152    /// written, and that on a failure contains a `BinaryError` object
153    ///
154    /// # Panics
155    ///
156    /// This method will panic if the binary file handle 
157    /// fails to be created successfully. 
158    pub fn write_to_file(&mut self) -> Result<usize, BinaryError> {
159        //ensure that the binary has an even number of bytes
160        if (self.length % 2) != 0 {
161            self.add_byte(0x00).unwrap();
162        }
163
164        //create the file
165        let mut file = match File::create(self.name.as_str()) {
166            Err(why) => panic!("Couldn't create {}: {:?}",
167                               self.name.as_str(), why),
168            Ok(file) => file 
169        };
170
171        //write the buffer to the file
172        let res = file.write(self.data.as_slice());
173
174        //and determine whether an error occurred
175        if res.is_ok() {
176            let size = res.unwrap();
177            return Ok(size);
178        } else {
179            return Err(BinaryError::new(BinaryErrorType::FileError,
180                                        self.name.as_str()));
181        }
182    }
183}
184