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