use std::num::NonZeroU16; pub struct Screen { pub(crate) vector: u16, pub(crate) width: u16, pub(crate) height: u16, pub(crate) auto: u8, pub(crate) x: u16, pub(crate) y: u16, pub(crate) addr: u16, pub(crate) palette: Palette, fg: Layer, bg: Layer, buffer: Vec, handler: F, } // [ // 0bR0_R1_R2_R3_R4_R5_R6_R7__R8_R9_Ra_Rb_Rc_Rd_Re_Rf, // 0bG0_G1_G2_G3_G4_G5_G6_G7__G8_G9_Ga_Gb_Gc_Gd_Ge_Gf, // 0bB0_B1_B2_B3_B4_B5_B6_B7__B8_B9_Ba_Bb_Bc_Bd_Be_Bf, // ] // <=> // [ // 0xXX_XX_XX_XX_R0_R1_R2_R3_R0_R1_R2_R3_G0_G1_G2_G3_G0_G1_G2_G3_B0_B1_B2_B3_B0_B1_B2_B3, // 0xXX_XX_XX_XX_R4_R5_R6_R7_R4_R5_R6_R7_G4_G5_G6_G7_G4_G5_G6_G7_B4_B5_B6_B7_B4_B5_B6_B7, // 0xXX_XX_XX_XX_R8_R9_Ra_Rb_R8_R9_Ra_Rb_G8_G9_Ga_Gb_G8_G9_Ga_Gb_B8_B9_Ba_Bb_B8_B9_Ba_Bb, // 0xXX_XX_XX_XX_Rc_Rd_Re_Rf_Rc_Rf_Re_Rf_Gc_Gd_Ge_Gf_Gc_Gd_Ge_Gf_Bc_Bd_Be_Bf_Bc_Bd_Be_Bf, // ] #[derive(Default)] pub struct Palette([u32; 4]); impl Palette { pub fn r_msb(&self) -> u8 { (((self.0[0] & 0x00f00000) >> 16) | ((self.0[1] & 0x000f0000) >> 16)) as u8 } pub fn r_lsb(&self) -> u8 { (((self.0[2] & 0x00f00000) >> 16) | ((self.0[3] & 0x000f0000) >> 16)) as u8 } pub fn g_msb(&self) -> u8 { (((self.0[0] & 0x0000f000) >> 8) | ((self.0[1] & 0x00000f00) >> 8)) as u8 } pub fn g_lsb(&self) -> u8 { (((self.0[2] & 0x0000f000) >> 8) | ((self.0[3] & 0x00000f00) >> 8)) as u8 } pub fn b_msb(&self) -> u8 { ((self.0[0] & 0x000000f0) | (self.0[1] & 0x0000000f)) as u8 } pub fn b_lsb(&self) -> u8 { ((self.0[2] & 0x000000f0) | (self.0[3] & 0x0000000f)) as u8 } pub fn set_r_msb(&mut self, val: u8) { let msn = (val >> 4) as u32; let lsn = (val & 0x0f) as u32; self.0[0] = (self.0[0] & 0xff00ffff) | (msn << 16) | (msn << 20); self.0[1] = (self.0[1] & 0xff00ffff) | (lsn << 16) | (lsn << 20); } pub fn set_r_lsb(&mut self, val: u8) { let msn = (val >> 4) as u32; let lsn = (val & 0x0f) as u32; self.0[2] = (self.0[2] & 0xff00ffff) | (msn << 16) | (msn << 20); self.0[3] = (self.0[3] & 0xff00ffff) | (lsn << 16) | (lsn << 20); } pub fn set_g_msb(&mut self, val: u8) { let msn = (val >> 4) as u32; let lsn = (val & 0x0f) as u32; self.0[0] = (self.0[0] & 0xffff00ff) | (msn << 8) | (msn << 12); self.0[1] = (self.0[1] & 0xffff00ff) | (lsn << 8) | (lsn << 12); } pub fn set_g_lsb(&mut self, val: u8) { let msn = (val >> 4) as u32; let lsn = (val & 0x0f) as u32; self.0[2] = (self.0[2] & 0xffff00ff) | (msn << 8) | (msn << 12); self.0[3] = (self.0[3] & 0xffff00ff) | (lsn << 8) | (lsn << 12); } pub fn set_b_msb(&mut self, val: u8) { let msn = (val >> 4) as u32; let lsn = (val & 0x0f) as u32; self.0[0] = (self.0[0] & 0xffffff00) | msn | (msn << 4); self.0[1] = (self.0[1] & 0xffffff00) | lsn | (lsn << 4); } pub fn set_b_lsb(&mut self, val: u8) { let msn = (val >> 4) as u32; let lsn = (val & 0x0f) as u32; self.0[2] = (self.0[2] & 0xffffff00) | msn | (msn << 4); self.0[3] = (self.0[3] & 0xffffff00) | lsn | (lsn << 4); } } #[derive(Default)] struct Layer { pixels: Vec, changed: bool, } impl Screen { pub fn new(handler: H) -> Self { Self { vector: Default::default(), width: Default::default(), height: Default::default(), auto: Default::default(), x: Default::default(), y: Default::default(), addr: Default::default(), palette: Default::default(), fg: Default::default(), bg: Default::default(), buffer: Default::default(), handler, } } } enum Sprite<'a> { Icn(&'a [u8; 8]), Chr(&'a [u8; 16]), } // impl<'a> Sprite<'a> { // fn into_iter(self, blending: u8, flip_x: bool, flip_y: bool) -> Pixels<'a> { // Pixels::new(self, blending, flip_x, flip_y) // } // } // struct Pixels<'a> { // v: u16, // h: u16, // c: u16, // sprite: Sprite<'a>, // blending: u8, // flip_x: bool, // flip_y: bool, // } // impl<'a> Pixels<'a> { // fn new(sprite: Sprite<'a>, blending: u8, flip_x: bool, flip_y: bool) -> Self { // Self { // v: 0, // h: 7, // c: Default::default(), // sprite, // blending, // flip_x, // flip_y, // } // } // } // impl Pixels<'_> { // const COLORS: [[u8; 16]; 4] = [ // [0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0], // [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], // [1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1], // [2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2], // ]; // } // impl<'a> Iterator for Pixels<'a> { // type Item = u8; // fn next(&mut self) -> Option { // if self.v == 8 { // return None; // } // self.c = match self.sprite { // Sprite::Icn(data) => data[self.v as usize] as u16, // Sprite::Chr(data) => { // let lsb = data[self.v as usize] as u16; // let msb = data[self.v as usize + 8] as u16; // (msb << 8) | lsb // } // }; // let ch = (self.c & 0x01) | ((self.c >> 7) & 0x02); // self.v += 1; // // for v in 0..8 { // // let mut c = sprite.index(v as usize); // // for h in (0..8).rev() { // // let ch = (c & 0x01) | ((c >> 7) & 0x02); // // if opaque || ch != 0 { // // let dx = if flip_x { 7 - h } else { h }; // // let dy = if flip_y { 7 - v } else { v }; // // self.write( // // width, // // height, // // x + dx, // // y + dy, // // COLORS[ch as usize][blending as usize], // // ); // // } // // c >>= 1; // // } // // } // } // } impl Sprite<'_> { fn index(&self, index: usize) -> u16 { match self { Sprite::Icn(data) => data[index] as u16, Sprite::Chr(data) => { let lsb = data[index] as u16; let msb = data[index + 8] as u16; (msb << 8) | lsb } } } } impl Screen { pub(crate) fn draw_pixel(&mut self, options: u8) { let layer = if options & 0x40 != 0 { &mut self.fg } else { &mut self.bg }; let color = options & 0x3; layer.write(self.width, self.height, self.x, self.y, color); if self.auto & 0x01 != 0 { self.x = self.x.wrapping_add(1); } if self.auto & 0x02 != 0 { self.y = self.y.wrapping_add(1); } } pub(crate) fn draw_sprite(&mut self, options: u8, ram: &[u8; 65536]) { let blending = options & 0x0f; let flip_x = options & 0x10 != 0; let flip_y = options & 0x20 != 0; let layer = if options & 0x40 != 0 { &mut self.fg } else { &mut self.bg }; let is_two_bpp = options & 0x80 != 0; let len = self.auto >> 4; let is_auto_x = self.auto & 0x01 != 0; let is_auto_y = self.auto & 0x02 != 0; let is_auto_addr = self.auto & 0x04 != 0; let dx = if is_auto_x { 8 } else { 0 }; let dy = if is_auto_y { 8 } else { 0 }; let da = if is_auto_addr { if is_two_bpp { 16 } else { 8 } } else { 0 }; let bytes_len = (len as u16 + 1) * if is_two_bpp { 16 } else { 8 }; if self.addr > 0xffff - bytes_len + 1 { return; } for i in 0..=len { if blending == 0x00 { layer.wipe( self.width, self.height, self.x + dy as u16 * i as u16, self.y + dx as u16 * i as u16, ) } else { let sprite = if is_two_bpp { Sprite::Chr( ram[self.addr as usize..self.addr as usize + 16] .try_into() .unwrap(), ) } else { Sprite::Icn( ram[self.addr as usize..self.addr as usize + 8] .try_into() .unwrap(), ) }; layer.blit( self.width, self.height, self.x + dy as u16 * i as u16, self.y + dx as u16 * i as u16, sprite, blending, flip_x, flip_y, ); self.addr += da; } } self.x += dx; self.y += dy; } } impl Layer { pub fn write(&mut self, width: u16, height: u16, x: u16, y: u16, color: u8) { if x < width && y < height { let i = y as usize * width as usize + x as usize; if color != self.pixels[i] { self.pixels[i] = color; self.changed = true; } } } pub fn wipe(&mut self, width: u16, height: u16, x: u16, y: u16) { for v in 0..8 { for h in 0..8 { self.write(width, height, x + h, y + v, 0); } } } pub fn blit( &mut self, width: u16, height: u16, x: u16, y: u16, sprite: Sprite, blending: u8, flip_x: bool, flip_y: bool, ) { const COLORS: [[u8; 16]; 4] = [ [0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0], [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], [1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1], [2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2], ]; let opaque = blending % 5 != 0 || blending == 0; for v in 0..8 { let mut c = sprite.index(v as usize); for h in (0..8).rev() { let ch = (c & 0x01) | ((c >> 7) & 0x02); if opaque || ch != 0 { let dx = if flip_x { 7 - h } else { h }; let dy = if flip_y { 7 - v } else { v }; self.write( width, height, x + dx, y + dy, COLORS[ch as usize][blending as usize], ); } c >>= 1; } } } } pub trait Handler { fn on_resize(&mut self, width: u32, height: u32); fn on_redraw(&mut self, bytes: &[u8], width: u32, height: u32); } impl Screen { pub fn set_size(&mut self, width: u16, height: u16) { self.width = width; self.height = height; let size = width as usize * height as usize; self.fg.pixels = vec![0; size]; self.fg.changed = true; self.bg.pixels = vec![0; size]; self.fg.changed = true; self.buffer = vec![0; size]; self.handler.on_resize(width as u32, height as u32); } pub fn adapt_size(&mut self) { self.set_size(self.width, self.height); } pub fn redraw(&mut self) { let palette: [u32; 16] = std::array::from_fn(|i| self.palette.0[if i >> 2 != 0 { i >> 2 } else { i & 3 }]); for ((pixel, fg_pixel), bg_pixel) in self .buffer .iter_mut() .zip(&self.fg.pixels) .zip(&self.bg.pixels) { *pixel = palette[(fg_pixel << 2 | bg_pixel) as usize]; } let (_, bytes, _) = unsafe { self.buffer.align_to::() }; self.handler .on_redraw(bytes, self.width as u32, self.height as u32); } } impl Screen { pub fn vector(&self) -> Option { NonZeroU16::new(self.vector) } pub fn width(&self) -> u16 { self.width } pub fn height(&self) -> u16 { self.height } }