summaryrefslogtreecommitdiff
path: root/src/stages/bytecode.rs
diff options
context:
space:
mode:
authorBen Bridle <bridle.benjamin@gmail.com>2025-02-14 09:36:52 +1300
committerBen Bridle <ben@derelict.engineering>2025-03-18 12:23:27 +1300
commit7d4dd52b8cfc865ae1b975ca3b6a3e72a812ebb9 (patch)
tree14ca9fa0ddcdd8c5155ddeaac241cd4f55486b6e /src/stages/bytecode.rs
parentf69a8f8c312ded212446082682bcabba8e3a9c9f (diff)
downloadbedrock-asm-7d4dd52b8cfc865ae1b975ca3b6a3e72a812ebb9.zip
Rewrite library
Diffstat (limited to 'src/stages/bytecode.rs')
-rw-r--r--src/stages/bytecode.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/src/stages/bytecode.rs b/src/stages/bytecode.rs
new file mode 100644
index 0000000..db6ff6d
--- /dev/null
+++ b/src/stages/bytecode.rs
@@ -0,0 +1,137 @@
+use crate::*;
+
+use std::collections::HashMap;
+
+
+/// Doesn't truncate trailing null bytes.
+pub fn generate_bytecode(semantic: &Program) -> AssembledProgram {
+ let mut generator = BytecodeGenerator::new(&semantic.definitions);
+ generator.parse(&semantic.tokens, false);
+ generator.fill_slots();
+ let mut symbols = Vec::new();
+ for (name, information) in generator.labels {
+ let source = semantic.definitions.get(&name).unwrap().source.clone();
+ let address = information.address;
+ symbols.push(AssembledSymbol { name, address, source });
+ }
+ AssembledProgram {
+ bytecode: generator.bytecode,
+ symbols,
+ }
+}
+
+
+pub struct BytecodeGenerator<'a> {
+ definitions: &'a HashMap<String, Tracked<Definition>>,
+ labels: HashMap<String, LabelInformation>,
+ stack: Vec<usize>,
+ bytecode: Vec<u8>,
+}
+
+struct LabelInformation {
+ address: usize,
+ slots: Vec<usize>,
+}
+
+impl<'a> BytecodeGenerator<'a> {
+ pub fn new(definitions: &'a HashMap<String, Tracked<Definition>>) -> Self {
+ let mut labels = HashMap::new();
+ for (name, definition) in definitions {
+ if let DefinitionKind::LabelDefinition = definition.kind {
+ labels.insert(name, LabelInformation { address: 0, slots: Vec::new() });
+ }
+ }
+ Self {
+ definitions,
+ labels: HashMap::new(),
+ stack: Vec::new(),
+ bytecode: Vec::new(),
+ }
+ }
+
+ pub fn parse(&mut self, tokens: &[Tracked<SemanticToken>], in_macro: bool) {
+ macro_rules! byte {
+ ($byte:expr) => {
+ self.bytecode.push($byte)
+ };
+ }
+ macro_rules! double {
+ ($double:expr) => {{
+ let [high, low] = u16::to_be_bytes($double);
+ self.bytecode.push(high);
+ self.bytecode.push(low);
+ }};
+ }
+
+ for token in tokens {
+ let i = self.bytecode.len();
+ match &token.value {
+ SemanticToken::Comment(_) => (),
+
+ SemanticToken::LabelDefinition(name) => if in_macro {
+ unreachable!("Uncaught label definition in macro");
+ } else {
+ let information = self.labels.get_mut(name).unwrap();
+ information.address = i;
+ }
+ SemanticToken::MacroDefinition{ .. } => if in_macro {
+ unreachable!("Uncaught macro definition in macro");
+ }
+
+ SemanticToken::RawValue(value) => match value {
+ Value::Byte(byte) => byte!(*byte),
+ Value::Double(double) => double!(*double),
+ }
+ SemanticToken::Instruction(instruction) => {
+ byte!(instruction.value)
+ }
+ SemanticToken::Invocation(name) => {
+ if let Some(definition) = self.definitions.get(name) {
+ match &definition.kind {
+ DefinitionKind::MacroDefinition(body) => {
+ self.parse(body, true);
+ }
+ DefinitionKind::LabelDefinition => {
+ let information = self.labels.get_mut(name).unwrap();
+ information.slots.push(i);
+ double!(0);
+ }
+ }
+ } else {
+ unreachable!("Uncaught undefined symbol '{name}'");
+ }
+ }
+
+ SemanticToken::Padding(value) => {
+ self.bytecode.resize(i + usize::from(value), 0);
+ },
+ SemanticToken::String(bytes) => {
+ self.bytecode.extend_from_slice(bytes)
+ },
+
+ SemanticToken::BlockOpen(_) => {
+ self.stack.push(i);
+ double!(0);
+ }
+ SemanticToken::BlockClose(_) => {
+ let Some(addr) = self.stack.pop() else {
+ unreachable!("Uncaught unmatched block terminator");
+ };
+ let [high, low] = (addr as u16).to_be_bytes();
+ self.bytecode[addr] = high;
+ self.bytecode[addr+1] = low;
+ }
+ }
+ }
+ }
+
+ pub fn fill_slots(&mut self) {
+ for information in self.labels.values() {
+ let [high, low] = (information.address as u16).to_be_bytes();
+ for addr in &information.slots {
+ self.bytecode[*addr] = high;
+ self.bytecode[*addr + 1] = low;
+ }
+ }
+ }
+}