From 1dbe79883c4eb7072812227a19b5e39dc9cbc268 Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Thu, 15 Jan 2026 17:01:25 +0000 Subject: [PATCH] add small boot management --- crates/core/src/lib.rs | 1 + crates/core/src/model/cartridge.rs | 9 +- crates/core/src/native_interface.rs | 5 +- crates/core/src/utilz.rs | 18 ++ crates/core/src/vm/mod.rs | 3 + crates/core/src/vm/program.rs | 17 + crates/core/src/vm/virtual_machine.rs | 449 +++++++++++++++++--------- docs/specs/topics/chapter-2.md | 35 +- 8 files changed, 363 insertions(+), 174 deletions(-) create mode 100644 crates/core/src/utilz.rs create mode 100644 crates/core/src/vm/program.rs diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index c1c93e4f..ad04a7b1 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -3,5 +3,6 @@ pub mod peripherals; pub mod vm; mod model; mod native_interface; +mod utilz; pub use machine::Machine; diff --git a/crates/core/src/model/cartridge.rs b/crates/core/src/model/cartridge.rs index 11d57a7b..bc02b3bf 100644 --- a/crates/core/src/model/cartridge.rs +++ b/crates/core/src/model/cartridge.rs @@ -1,13 +1,12 @@ -use crate::vm::Value; +use crate::vm::Program; #[derive(Clone, Debug)] pub struct Cartridge { - pub rom: Vec, - pub constant_pool: Vec, + pub program: Program, } impl Cartridge { - pub fn new(rom: Vec, constant_pool: Vec) -> Self { - Self { rom, constant_pool } + pub fn new(program: Program) -> Self { + Self { program } } } diff --git a/crates/core/src/native_interface.rs b/crates/core/src/native_interface.rs index 942e1812..e3f078a3 100644 --- a/crates/core/src/native_interface.rs +++ b/crates/core/src/native_interface.rs @@ -16,11 +16,12 @@ impl NativeInterface for Machine { // system.run_cart() 0x0002 => { if let Some(cart) = self.cartridge.as_ref() { - vm.rom = cart.rom.clone(); - vm.constant_pool = cart.constant_pool.clone(); + vm.program = cart.program.clone(); vm.pc = 0; vm.operand_stack.clear(); vm.call_stack.clear(); + vm.globals.clear(); + vm.heap.clear(); vm.halted = false; } else { return Err("No cartridge inserted".into()); diff --git a/crates/core/src/utilz.rs b/crates/core/src/utilz.rs new file mode 100644 index 00000000..21804c03 --- /dev/null +++ b/crates/core/src/utilz.rs @@ -0,0 +1,18 @@ +use crate::vm::OpCode; + +pub fn emit_u16(rom: &mut Vec, val: u16) { + rom.extend_from_slice(&val.to_le_bytes()); +} + +pub fn emit_u32(rom: &mut Vec, val: u32) { + rom.extend_from_slice(&val.to_le_bytes()); +} + +pub fn emit_op(rom: &mut Vec, op: OpCode) { + emit_u16(rom, op as u16); +} + +pub fn patch_u32(rom: &mut [u8], offset: usize, val: u32) { + let bytes = val.to_le_bytes(); + rom[offset..offset + 4].copy_from_slice(&bytes); +} diff --git a/crates/core/src/vm/mod.rs b/crates/core/src/vm/mod.rs index d8ef67dd..bc214d4f 100644 --- a/crates/core/src/vm/mod.rs +++ b/crates/core/src/vm/mod.rs @@ -2,6 +2,9 @@ mod virtual_machine; mod value; mod opcode; mod call_frame; +mod program; pub use value::Value; pub use virtual_machine::VirtualMachine; +pub use opcode::OpCode; +pub use program::Program; diff --git a/crates/core/src/vm/program.rs b/crates/core/src/vm/program.rs new file mode 100644 index 00000000..f92a9e6f --- /dev/null +++ b/crates/core/src/vm/program.rs @@ -0,0 +1,17 @@ +use std::sync::Arc; +use crate::vm::Value; + +#[derive(Debug, Clone, Default)] +pub struct Program { + pub rom: Arc<[u8]>, + pub constant_pool: Arc<[Value]>, +} + +impl Program { + pub fn new(rom: Vec, constant_pool: Vec) -> Self { + Self { + rom: Arc::from(rom), + constant_pool: Arc::from(constant_pool), + } + } +} diff --git a/crates/core/src/vm/virtual_machine.rs b/crates/core/src/vm/virtual_machine.rs index 247cda45..3b10f89b 100644 --- a/crates/core/src/vm/virtual_machine.rs +++ b/crates/core/src/vm/virtual_machine.rs @@ -1,16 +1,18 @@ use crate::native_interface::NativeInterface; +use crate::utilz; use crate::vm::call_frame::CallFrame; use crate::vm::opcode::OpCode; use crate::vm::value::Value; +use crate::vm::Program; + pub struct VirtualMachine { pub pc: usize, pub operand_stack: Vec, pub call_stack: Vec, pub globals: Vec, - pub constant_pool: Vec, - pub rom: Vec, + pub program: Program, pub heap: Vec, // Simplificado para demo, futuramente RAM/Heap real pub cycles: u64, pub halted: bool, @@ -23,8 +25,7 @@ impl VirtualMachine { operand_stack: Vec::new(), call_stack: Vec::new(), globals: Vec::new(), - constant_pool, - rom, + program: Program::new(rom, constant_pool), heap: Vec::new(), cycles: 0, halted: false, @@ -37,95 +38,231 @@ impl VirtualMachine { let mut rom = Vec::new(); let mut constant_pool = Vec::new(); - // Constantes do Boot ROM - constant_pool.push(Value::Integer(140)); // 0: x - constant_pool.push(Value::Integer(70)); // 1: y - constant_pool.push(Value::Integer(40)); // 2: largura - constant_pool.push(Value::Integer(40)); // 3: altura - constant_pool.push(Value::Integer(7)); // 4: cor rect (indigo) - constant_pool.push(Value::Integer(0)); // 5: sample_id (square) - constant_pool.push(Value::Integer(0)); // 6: voice_id - constant_pool.push(Value::Integer(255)); // 7: volume - constant_pool.push(Value::Integer(127)); // 8: pan (center) - constant_pool.push(Value::Float(1.0)); // 9: pitch - constant_pool.push(Value::Integer(0)); // 10: bg color (black) - constant_pool.push(Value::Integer(10)); // 11: button START + // Constant Pool + constant_pool.push(Value::Integer(160)); // 0: Center X + constant_pool.push(Value::Integer(90)); // 1: Center Y + constant_pool.push(Value::Integer(40)); // 2: Logo Size + constant_pool.push(Value::Integer(7)); // 3: Indigo (Logo) + constant_pool.push(Value::Integer(0)); // 4: Black (Background) + constant_pool.push(Value::Integer(180)); // 5: Splash Timeout (3s) + constant_pool.push(Value::Integer(2)); // 6: Divisor + constant_pool.push(Value::Integer(1)); // 7: 1 + constant_pool.push(Value::Integer(10)); // 8: Start Button + constant_pool.push(Value::Integer(0)); // 9: Square Sample + constant_pool.push(Value::Integer(255)); // 10: Volume + constant_pool.push(Value::Integer(127)); // 11: Pan + constant_pool.push(Value::Float(1.0)); // 12: Pitch + constant_pool.push(Value::Integer(0)); // 13: Voice ID + constant_pool.push(Value::Integer(0)); // 14: Zero + constant_pool.push(Value::Integer(6)); // 15: Cyan (SO Logo) - // -- PROGRAMA -- + // -- INICIALIZAÇÃO -- - // 1. Toca o som de boot "plim" (uma vez) - // Push arguments for audio.play_sample(sample_id, voice_id, volume, pan, pitch) - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&5u32.to_le_bytes()); // sample_id - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&6u32.to_le_bytes()); // voice_id - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&7u32.to_le_bytes()); // volume - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&8u32.to_le_bytes()); // pan - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&9u32.to_le_bytes()); // pitch - rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - rom.extend_from_slice(&0x3001u32.to_le_bytes()); + // G0 = 0 (Contador de frames) + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 14); + utilz::emit_op(&mut rom, OpCode::SetGlobal); + utilz::emit_u32(&mut rom, 0); + + // G1 = 0 (Estado BIOS: 0=Splash, 1=SO) + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 14); + utilz::emit_op(&mut rom, OpCode::SetGlobal); + utilz::emit_u32(&mut rom, 1); + + // Toca o som de boot "plim" + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 9); // sample + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 13); // voice + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 10); // vol + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 11); // pan + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 12); // pitch + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x3001); + + // Escopo local para cálculos temporários [0:f, 1:s, 2:x, 3:y] + utilz::emit_op(&mut rom, OpCode::PushScope); + utilz::emit_u32(&mut rom, 4); let loop_start = rom.len() as u32; - // 2. Verifica Cartucho e Input para Boot - // system.has_cart? - rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - rom.extend_from_slice(&0x0001u32.to_le_bytes()); - - let jmp_no_cart_idx = rom.len(); - rom.extend_from_slice(&(OpCode::JmpIfFalse as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); // placeholder - - // Se tem cartucho, checa START - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&11u32.to_le_bytes()); // START button ID - rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - rom.extend_from_slice(&0x2001u32.to_le_bytes()); // input.get_pad - - let jmp_no_start_idx = rom.len(); - rom.extend_from_slice(&(OpCode::JmpIfFalse as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); // placeholder - - // Se tem cartucho E START, run_cart! - rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - rom.extend_from_slice(&0x0002u32.to_le_bytes()); // system.run_cart - - // Destino para quando não tem cartucho ou não apertou START - let skip_cart_addr = rom.len() as u32; - // Patch placeholders - let skip_bytes = skip_cart_addr.to_le_bytes(); - rom[jmp_no_cart_idx+2..jmp_no_cart_idx+6].copy_from_slice(&skip_bytes); - rom[jmp_no_start_idx+2..jmp_no_start_idx+6].copy_from_slice(&skip_bytes); + // Limpa tela + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 4); // black + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x1001); - // 3. Limpa a tela - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&10u32.to_le_bytes()); // bg color - rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - rom.extend_from_slice(&0x1001u32.to_le_bytes()); + // --- MÁQUINA DE ESTADOS (G1) --- + utilz::emit_op(&mut rom, OpCode::GetGlobal); + utilz::emit_u32(&mut rom, 1); // G1 + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 14); // 0 + utilz::emit_op(&mut rom, OpCode::Eq); + let jmp_to_so = rom.len(); + utilz::emit_op(&mut rom, OpCode::JmpIfFalse); + utilz::emit_u32(&mut rom, 0); - // 4. Desenha o quadrado no centro - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); // x - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&1u32.to_le_bytes()); // y - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&2u32.to_le_bytes()); // w - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&3u32.to_le_bytes()); // h - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&4u32.to_le_bytes()); // color - rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - rom.extend_from_slice(&0x1002u32.to_le_bytes()); + // === ESTADO 0: SPLASH === + + // 1. Cálculos da Animação + utilz::emit_op(&mut rom, OpCode::GetGlobal); + utilz::emit_u32(&mut rom, 0); + utilz::emit_op(&mut rom, OpCode::SetLocal); + utilz::emit_u32(&mut rom, 0); // L0 = f - // 4. Sincroniza frame - rom.extend_from_slice(&(OpCode::FrameSync as u16).to_le_bytes()); + // S = if f < 40 { f } else { 40 } + utilz::emit_op(&mut rom, OpCode::GetLocal); + utilz::emit_u32(&mut rom, 0); // f + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 2); // 40 + utilz::emit_op(&mut rom, OpCode::Lt); + let jmp_s_full = rom.len(); + utilz::emit_op(&mut rom, OpCode::JmpIfFalse); + utilz::emit_u32(&mut rom, 0); - // 5. Loop infinito - rom.extend_from_slice(&(OpCode::Jmp as u16).to_le_bytes()); - rom.extend_from_slice(&loop_start.to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::GetLocal); + utilz::emit_u32(&mut rom, 0); // f + let jmp_s_set = rom.len(); + utilz::emit_op(&mut rom, OpCode::Jmp); + utilz::emit_u32(&mut rom, 0); + + let s_full_addr = rom.len() as u32; + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 2); // 40 + + let s_set_addr = rom.len() as u32; + utilz::patch_u32(&mut rom, jmp_s_full + 2, s_full_addr); + utilz::patch_u32(&mut rom, jmp_s_set + 2, s_set_addr); + utilz::emit_op(&mut rom, OpCode::SetLocal); + utilz::emit_u32(&mut rom, 1); // L1 = S + + // X = 160 - S/2, Y = 90 - S/2 + for (local_idx, const_idx) in [(2, 0), (3, 1)] { + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, const_idx); + utilz::emit_op(&mut rom, OpCode::GetLocal); + utilz::emit_u32(&mut rom, 1); // S + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 6); // 2 + utilz::emit_op(&mut rom, OpCode::Div); + utilz::emit_op(&mut rom, OpCode::Sub); + utilz::emit_op(&mut rom, OpCode::SetLocal); + utilz::emit_u32(&mut rom, local_idx); + } + + // Desenha Quadrado (Logo) + utilz::emit_op(&mut rom, OpCode::GetLocal); + utilz::emit_u32(&mut rom, 2); // X + utilz::emit_op(&mut rom, OpCode::GetLocal); + utilz::emit_u32(&mut rom, 3); // Y + utilz::emit_op(&mut rom, OpCode::GetLocal); + utilz::emit_u32(&mut rom, 1); // W + utilz::emit_op(&mut rom, OpCode::GetLocal); + utilz::emit_u32(&mut rom, 1); // H + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 3); // Indigo + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x1002); + + // Checagem de Saída do Splash (f >= 180 OR START) + utilz::emit_op(&mut rom, OpCode::GetGlobal); + utilz::emit_u32(&mut rom, 0); // f + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 5); // 180 + utilz::emit_op(&mut rom, OpCode::Gt); // f > 180 (ou eq, mas gt é mais seguro) + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 8); // Start + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x2001); // get_pad + utilz::emit_op(&mut rom, OpCode::Or); + let jmp_splash_continue = rom.len(); + utilz::emit_op(&mut rom, OpCode::JmpIfFalse); + utilz::emit_u32(&mut rom, 0); + + // Validação de Cartucho + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x0001); // has_cart? + let jmp_no_cart_init = rom.len(); + utilz::emit_op(&mut rom, OpCode::JmpIfFalse); + utilz::emit_u32(&mut rom, 0); + + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x0002); // run_cart + + // Senão tem cartucho: vai para o SO + let no_cart_init_addr = rom.len() as u32; + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 7); // 1 + utilz::emit_op(&mut rom, OpCode::SetGlobal); + utilz::emit_u32(&mut rom, 1); // G1 = 1 (SO) + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 14); // 0 + utilz::emit_op(&mut rom, OpCode::SetGlobal); + utilz::emit_u32(&mut rom, 0); // f = 0 (reset counter for SO) + + let splash_continue_addr = rom.len() as u32; + utilz::patch_u32(&mut rom, jmp_splash_continue + 2, splash_continue_addr); + utilz::patch_u32(&mut rom, jmp_no_cart_init + 2, no_cart_init_addr); + + let jmp_end_state = rom.len(); + utilz::emit_op(&mut rom, OpCode::Jmp); + utilz::emit_u32(&mut rom, 0); + + // === ESTADO 1: SO === + let so_state_addr = rom.len() as u32; + utilz::patch_u32(&mut rom, jmp_to_so + 2, so_state_addr); + + // Desenha "SO" (Quadrado Ciano) + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 0); // 160 + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 1); // 90 + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 2); // 40 + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 2); // 40 + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 15); // Cyan + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x1002); + + // Monitora Inserção de Cartucho (Hot-plug) + START + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x0001); // has_cart? + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 8); // Start + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x2001); // get_pad + utilz::emit_op(&mut rom, OpCode::And); // has_cart AND START + let jmp_so_continue = rom.len(); + utilz::emit_op(&mut rom, OpCode::JmpIfFalse); + utilz::emit_u32(&mut rom, 0); + + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x0002); // run_cart + + let so_continue_addr = rom.len() as u32; + utilz::patch_u32(&mut rom, jmp_so_continue + 2, so_continue_addr); + + // Incrementa G0 (f) + let end_state_addr = rom.len() as u32; + utilz::patch_u32(&mut rom, jmp_end_state + 2, end_state_addr); + + utilz::emit_op(&mut rom, OpCode::GetGlobal); + utilz::emit_u32(&mut rom, 0); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 7); // 1 + utilz::emit_op(&mut rom, OpCode::Add); + utilz::emit_op(&mut rom, OpCode::SetGlobal); + utilz::emit_u32(&mut rom, 0); + + utilz::emit_op(&mut rom, OpCode::FrameSync); + utilz::emit_op(&mut rom, OpCode::Jmp); + utilz::emit_u32(&mut rom, loop_start); Self::new(rom, constant_pool) } @@ -142,7 +279,7 @@ impl VirtualMachine { let start_cycles = self.cycles; let mut budget_used = 0; - while budget_used < budget && !self.halted && self.pc < self.rom.len() { + while budget_used < budget && !self.halted && self.pc < self.program.rom.len() { let pc_before = self.pc; let cycles_before = self.cycles; let opcode_val = self.peek_u16()?; @@ -168,18 +305,18 @@ impl VirtualMachine { } fn peek_u16(&self) -> Result { - if self.pc + 2 > self.rom.len() { + if self.pc + 2 > self.program.rom.len() { return Err("Unexpected end of ROM".into()); } let bytes = [ - self.rom[self.pc], - self.rom[self.pc + 1], + self.program.rom[self.pc], + self.program.rom[self.pc + 1], ]; Ok(u16::from_le_bytes(bytes)) } pub fn step(&mut self, native: &mut dyn NativeInterface) -> Result<(), String> { - if self.halted || self.pc >= self.rom.len() { + if self.halted || self.pc >= self.program.rom.len() { return Ok(()); } @@ -204,7 +341,7 @@ impl VirtualMachine { } OpCode::PushConst => { let idx = self.read_u32()? as usize; - let val = self.constant_pool.get(idx).cloned().ok_or("Invalid constant index")?; + let val = self.program.constant_pool.get(idx).cloned().ok_or("Invalid constant index")?; self.push(val); } OpCode::Pop => { @@ -405,26 +542,26 @@ impl VirtualMachine { } fn read_u32(&mut self) -> Result { - if self.pc + 4 > self.rom.len() { + if self.pc + 4 > self.program.rom.len() { return Err("Unexpected end of ROM".into()); } let bytes = [ - self.rom[self.pc], - self.rom[self.pc + 1], - self.rom[self.pc + 2], - self.rom[self.pc + 3], + self.program.rom[self.pc], + self.program.rom[self.pc + 1], + self.program.rom[self.pc + 2], + self.program.rom[self.pc + 3], ]; self.pc += 4; Ok(u32::from_le_bytes(bytes)) } fn read_u16(&mut self) -> Result { - if self.pc + 2 > self.rom.len() { + if self.pc + 2 > self.program.rom.len() { return Err("Unexpected end of ROM".into()); } let bytes = [ - self.rom[self.pc], - self.rom[self.pc + 1], + self.program.rom[self.pc], + self.program.rom[self.pc + 1], ]; self.pc += 2; Ok(u16::from_le_bytes(bytes)) @@ -477,12 +614,12 @@ mod tests { let mut native = NoopNative; let mut rom = Vec::new(); - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); // Const index 0 - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&1u32.to_le_bytes()); // Const index 1 - rom.extend_from_slice(&(OpCode::Add as u16).to_le_bytes()); - rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 0); // Const index 0 + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 1); // Const index 1 + utilz::emit_op(&mut rom, OpCode::Add); + utilz::emit_op(&mut rom, OpCode::Halt); let constant_pool = vec![Value::Integer(10), Value::Integer(20)]; let mut vm = VirtualMachine::new(rom, constant_pool); @@ -504,39 +641,39 @@ mod tests { let mut rom = Vec::new(); // Index 0: PUSH 0 (counter) - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 0); // Index 6: DUP let loop_start = rom.len(); - rom.extend_from_slice(&(OpCode::Dup as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::Dup); // PUSH 10 - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&1u32.to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 1); // LT (counter < 10) - rom.extend_from_slice(&(OpCode::Lt as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::Lt); // JMP_IF_FALSE to end - rom.extend_from_slice(&(OpCode::JmpIfFalse as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::JmpIfFalse); let jmp_placeholder = rom.len(); - rom.extend_from_slice(&0u32.to_le_bytes()); + utilz::emit_u32(&mut rom, 0); // PUSH 1 - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&2u32.to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 2); // ADD (increment counter) - rom.extend_from_slice(&(OpCode::Add as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::Add); // JMP to start - rom.extend_from_slice(&(OpCode::Jmp as u16).to_le_bytes()); - rom.extend_from_slice(&(loop_start as u32).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::Jmp); + utilz::emit_u32(&mut rom, loop_start as u32); // End let loop_end = rom.len(); - rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::Halt); // Patch JMP_IF_FALSE addr let end_addr_bytes = (loop_end as u32).to_le_bytes(); @@ -571,12 +708,12 @@ mod tests { let mut rom = Vec::new(); // PUSH 5 (color index) - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 0); // CALL_NATIVE 0x1001 - rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - rom.extend_from_slice(&0x1001u32.to_le_bytes()); - rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x1001); + utilz::emit_op(&mut rom, OpCode::Halt); let constant_pool = vec![Value::Integer(5)]; let mut vm = VirtualMachine::new(rom, constant_pool); @@ -597,9 +734,9 @@ mod tests { // 1. Verifica que não tem cartucho inicialmente let mut rom = Vec::new(); // CALL_NATIVE 0x0001 (has_cart) - rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - rom.extend_from_slice(&0x0001u32.to_le_bytes()); - rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::Syscall); + utilz::emit_u32(&mut rom, 0x0001); + utilz::emit_op(&mut rom, OpCode::Halt); machine.vm = VirtualMachine::new(rom, vec![]); let mut vm = std::mem::take(&mut machine.vm); @@ -611,19 +748,19 @@ mod tests { // 2. Adiciona um cartucho e roda let mut cart_rom = Vec::new(); // PUSH_CONST 0 - cart_rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - cart_rom.extend_from_slice(&0u32.to_le_bytes()); - cart_rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + utilz::emit_op(&mut cart_rom, OpCode::PushConst); + utilz::emit_u32(&mut cart_rom, 0); + utilz::emit_op(&mut cart_rom, OpCode::Halt); let cart_pool = vec![Value::Integer(42)]; - let cart = Cartridge::new(cart_rom, cart_pool); + let cart = Cartridge::new(Program::new(cart_rom, cart_pool)); machine.load_cartridge(cart); // Código para rodar o cartucho let mut boot_rom = Vec::new(); // CALL_NATIVE 0x0002 (run_cart) - boot_rom.extend_from_slice(&(OpCode::Syscall as u16).to_le_bytes()); - boot_rom.extend_from_slice(&0x0002u32.to_le_bytes()); + utilz::emit_op(&mut boot_rom, OpCode::Syscall); + utilz::emit_u32(&mut boot_rom, 0x0002); machine.vm = VirtualMachine::new(boot_rom, vec![]); let mut vm = std::mem::take(&mut machine.vm); @@ -645,13 +782,13 @@ mod tests { let mut rom = Vec::new(); // PUSH 10, PUSH 20, SWAP, POP - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&1u32.to_le_bytes()); - rom.extend_from_slice(&(OpCode::Swap as u16).to_le_bytes()); - rom.extend_from_slice(&(OpCode::Pop as u16).to_le_bytes()); - rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 0); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 1); + utilz::emit_op(&mut rom, OpCode::Swap); + utilz::emit_op(&mut rom, OpCode::Pop); + utilz::emit_op(&mut rom, OpCode::Halt); let constant_pool = vec![Value::Integer(10), Value::Integer(20)]; let mut vm = VirtualMachine::new(rom, constant_pool); @@ -671,13 +808,13 @@ mod tests { let mut rom = Vec::new(); // PUSH true, NOT (-> false), PUSH true, OR (-> true) - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); // true - rom.extend_from_slice(&(OpCode::Not as u16).to_le_bytes()); - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); // true - rom.extend_from_slice(&(OpCode::Or as u16).to_le_bytes()); - rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 0); // true + utilz::emit_op(&mut rom, OpCode::Not); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 0); // true + utilz::emit_op(&mut rom, OpCode::Or); + utilz::emit_op(&mut rom, OpCode::Halt); let constant_pool = vec![Value::Boolean(true)]; let mut vm = VirtualMachine::new(rom, constant_pool); @@ -696,16 +833,16 @@ mod tests { let mut rom = Vec::new(); // PUSH_SCOPE 2 (reserves 2 nulls), PUSH 42, SET_LOCAL 0, GET_LOCAL 0, POP_SCOPE - rom.extend_from_slice(&(OpCode::PushScope as u16).to_le_bytes()); - rom.extend_from_slice(&2u32.to_le_bytes()); - rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); // 42 - rom.extend_from_slice(&(OpCode::SetLocal as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); - rom.extend_from_slice(&(OpCode::GetLocal as u16).to_le_bytes()); - rom.extend_from_slice(&0u32.to_le_bytes()); - rom.extend_from_slice(&(OpCode::PopScope as u16).to_le_bytes()); - rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + utilz::emit_op(&mut rom, OpCode::PushScope); + utilz::emit_u32(&mut rom, 2); + utilz::emit_op(&mut rom, OpCode::PushConst); + utilz::emit_u32(&mut rom, 0); // 42 + utilz::emit_op(&mut rom, OpCode::SetLocal); + utilz::emit_u32(&mut rom, 0); + utilz::emit_op(&mut rom, OpCode::GetLocal); + utilz::emit_u32(&mut rom, 0); + utilz::emit_op(&mut rom, OpCode::PopScope); + utilz::emit_op(&mut rom, OpCode::Halt); let constant_pool = vec![Value::Integer(42)]; let mut vm = VirtualMachine::new(rom, constant_pool); diff --git a/docs/specs/topics/chapter-2.md b/docs/specs/topics/chapter-2.md index cb97763f..40ca40f1 100644 --- a/docs/specs/topics/chapter-2.md +++ b/docs/specs/topics/chapter-2.md @@ -373,18 +373,31 @@ Efeito prático: A **PROMETEU VM** nunca inicia em um estado totalmente "vazio" ou inativo. -### 13.1 O Boot ROM -Se a máquina for inicializada sem um cartucho específico carregado, a VM executa um **Boot ROM padrão**. Este é um pequeno programa em bytecode embutido no core que: -* Emite um som de "plim" ao iniciar. -* Realiza um ciclo básico de limpeza de tela (preto). -* Exibe um quadrado centralizado (índigo). -* Serve como indicador visual e auditivo de que o hardware lógico está operacional. +### 13.1 O Boot ROM (BIOS) +Se a máquina for inicializada sem um cartucho específico carregado, a VM executa um **Boot ROM padrão**. Este é um pequeno programa em bytecode embutido no core que gerencia o ciclo de vida inicial da máquina através de uma máquina de estados: -### 13.2 Ciclo de Boot -1. O Core é inicializado via `Machine::new()`. -2. A VM é carregada com o Boot ROM via `VirtualMachine::default()`. -3. O PC (Program Counter) é definido como `0`. -4. A execução começa imediatamente no primeiro `step_frame`. +#### Fase 1: BOOT (Splash & Som) +1. **Som de Inicialização**: Emite um som de "plim" (`0x3001`) imediatamente ao ligar (frame 0). +2. **Splash Animation**: Exibe o logotipo (quadrado Índigo) crescendo suavemente (0-40 frames). +3. **Splash Estático**: O logotipo permanece na tela até o frame 180 (~3 segundos). +4. **Interrupção**: O usuário pode pular o Splash a qualquer momento pressionando **START**. + +#### Fase 2: C_VALIDATION (Validação) +Ao fim do Splash ou interrupção, a BIOS verifica a presença de um cartucho: +- **Com Cartucho**: Chama `system.run_cart` (`0x0002`) para transferir o controle. +- **Sem Cartucho**: Transita para o **Estado de SO**. + +#### Fase 3: RUN (Sistema Operacional / Fallback) +Se nenhum cartucho for detectado, a máquina entra em modo "SO": +1. **Interface**: Exibe um quadrado **Ciano** no centro da tela. +2. **Hot-plug**: Monitora em tempo real a inserção de cartuchos. Se um cartucho for inserido e o botão **START** for pressionado, o cartucho é iniciado. + +### 13.2 Ciclo de Memória +A instrução `system.run_cart` garante um ambiente limpo para o novo programa: +1. Reseta o **PC** para 0. +2. Limpa as pilhas (**Operand Stack** e **Call Stack**). +3. Limpa a memória (**Globals** e **Heap**). +4. Carrega a nova **ROM** e **Constant Pool** do cartucho. ---