use crate::peripherals::{BlendMode, Gfx, InputSignals, Pad, Touch}; use crate::Color; /// PROMETEU "hardware lógico" (v0). /// O Host alimenta INPUT SIGNALS e chama `step_frame()` em 60Hz. pub struct Machine { pub gfx: Gfx, pub pad: Pad, pub touch: Touch, pub frame_index: u64, } impl Machine { pub const W: usize = 320; pub const H: usize = 180; pub fn new() -> Self { Self { gfx: Gfx::new(Self::W, Self::H), pad: Pad::default(), touch: Touch::default(), frame_index: 0, } } /// "Contrato de frame" do PROMETEU (Template Method sem loop). /// O Host controla tempo/event loop; o Core define a sequência do frame. pub fn step_frame(&mut self, signals: &InputSignals) { self.begin_frame(signals); self.tick(); // futuro: executa cartucho/VM self.end_frame(); // present/housekeeping } /// Início do frame: zera flags transitórias. pub fn begin_frame(&mut self, signals: &InputSignals) { self.frame_index += 1; // Flags transitórias do TOUCH devem durar 1 frame. self.touch.begin_frame(signals); // Se você quiser input com pressed/released depois: self.pad.begin_frame(signals); } /// Lógica do frame (demo hardcoded por enquanto). pub fn tick(&mut self) { self.gfx.clear(Color::rgb(0x10, 0x10, 0x10)); let (x, y) = self.demo_pos(); let color = if self.pad.a.down || self.touch.f.down { Color::rgb(0x00, 0xFF, 0x00) } else { Color::rgb(0xFF, 0x40, 0x40) }; // game self.gfx.fill_rect(x, y, 20, 20, color); // smoke self.gfx.fill_rect_blend(140, 0, 40, 180, Color::gray_scale(0x88), BlendMode::Half) } /// Final do frame: troca buffers. pub fn end_frame(&mut self) { self.gfx.present(); } fn demo_pos(&self) -> (i32, i32) { let x = (self.frame_index % 300) as i32; let y = (self.pad.a.hold_frames % 160) as i32; (x, y) } }