Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 24 additions & 73 deletions clicky-core/src/devices/display/hd66753.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::sync::{Arc, RwLock};
use relativity::Instant;

use crate::devices::display::LcdPanel;
use crate::gui::RenderCallback;

use either::Either;
Expand Down Expand Up @@ -91,10 +92,6 @@

/// Hitachi HD66753 168x132 monochrome LCD Controller.
pub struct Hd66753 {
// FIXME: not sure if there are separate latches for the command and data registers...
write_byte_latch: Option<u8>,
read_byte_latch: Option<u8>,

/// Index Register
ir: u16,
/// Address counter
Expand All @@ -108,8 +105,6 @@
impl std::fmt::Debug for Hd66753 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Hd66753")
.field("write_byte_latch", &self.write_byte_latch)
.field("read_byte_latch", &self.read_byte_latch)
.field("ir", &self.ir)
.field("ac", &self.ac)
.field("cgram", &"[...]")
Expand All @@ -130,8 +125,6 @@
ir: 0,
ac: 0,
cgram,
write_byte_latch: None,
read_byte_latch: None,
ireg,
}
}
Expand All @@ -149,7 +142,7 @@
///
/// The callback accepts a minifb framebuffer, and returns the rendered
/// dimensions.
pub fn render_callback(&self) -> RenderCallback {
fn make_render_callback(&self) -> RenderCallback {
let cgram = Arc::clone(&self.cgram);
let ireg = Arc::clone(&self.ireg);
let start = Instant::now();
Expand All @@ -164,7 +157,7 @@
let ireg = *ireg.read().unwrap();

// Hardcoded to 1Hz for now
let blink_on = (start.elapsed().as_millis() / 500) % 2 == 0;

Check warning on line 160 in clicky-core/src/devices/display/hd66753.rs

View workflow job for this annotation

GitHub Actions / clippy

manual implementation of `.is_multiple_of()`

warning: manual implementation of `.is_multiple_of()` --> clicky-core/src/devices/display/hd66753.rs:160:28 | 160 | let blink_on = (start.elapsed().as_millis() / 500) % 2 == 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(start.elapsed().as_millis() / 500).is_multiple_of(2)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#manual_is_multiple_of = note: `#[warn(clippy::manual_is_multiple_of)]` on by default

let height = match ireg.nl {
0b11111 => 132,
Expand All @@ -172,7 +165,7 @@
};

let cgram_window = cgram
.chunks_exact(EMU_CGRAM_WIDTH * 2 / 8 / 2)

Check warning on line 168 in clicky-core/src/devices/display/hd66753.rs

View workflow job for this annotation

GitHub Actions / clippy

using `chunks_exact` with a constant chunk size

warning: using `chunks_exact` with a constant chunk size --> clicky-core/src/devices/display/hd66753.rs:168:22 | 168 | .chunks_exact(EMU_CGRAM_WIDTH * 2 / 8 / 2) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `as_chunks` instead: `as_chunks::<EMU_CGRAM_WIDTH * 2 / 8 / 2>().0.iter()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks = note: `#[warn(clippy::chunks_exact_to_as_chunks)]` on by default
.take(height)
.flat_map(|row| {
match ireg.sgs {
Expand Down Expand Up @@ -429,77 +422,35 @@
}
}

impl Device for Hd66753 {
fn kind(&self) -> &'static str {
"HD 66753"
}

fn probe(&self, offset: u32) -> Probe {
let reg = match offset {
0x0 => "LCD Control",
0x8 => "LCD Command",
0x10 => "LCD Data",
_ => return Probe::Unmapped,
};

Probe::Register(reg)
}
}

impl Memory for Hd66753 {
fn r32(&mut self, offset: u32) -> MemResult<u32> {
if offset == 0x0 {
// bypass the latch
return Ok(0); // HACK: Emulated LCD is never busy
}
impl LcdPanel for Hd66753 {
fn write_command(&mut self, val: u16) -> MemResult<()> {
self.ir = val;

if let Some(val) = self.read_byte_latch.take() {
return Ok(val as u32);
if self.ir > 0x12 {
return Err(ContractViolation {
msg: format!("set invalid LCD Command: {:#04x?}", val),
severity: Error,
stub_val: None,
});
}

let val: u16 = match offset {
// XXX: not currently tracking driving raster-row position
0x8 => self.ireg.read().unwrap().ct as u16,
0x10 => self.handle_data_read()?,
_ => return Err(Unexpected),
};

self.read_byte_latch = Some(val as u8); // latch lower 8 bits
Ok((val >> 8) as u32) // returning the higher 8 bits first
Ok(())
}

fn w32(&mut self, offset: u32, val: u32) -> MemResult<()> {
if offset == 0x0 {
// bypass the latch
return Err(StubWrite(Error, ()));
}

// the iPod uses the controller via an 8-bit interface
let val = val as u8; // FIXME: this should use trunc_to_u8, but it crashes...
let val = match self.write_byte_latch.take() {
None => {
self.write_byte_latch = Some(val);
return Ok(());
}
Some(hi) => (hi as u16) << 8 | (val as u16),
};
fn read_command(&mut self) -> MemResult<u16> {
// XXX: not currently tracking driving raster-row position
Ok(self.ireg.read().unwrap().ct as u16)
}

match offset {
0x8 => {
self.ir = val;
fn write_data(&mut self, val: u16) -> MemResult<()> {
self.handle_data_write(val)
}

if self.ir > 0x12 {
return Err(ContractViolation {
msg: format!("set invalid LCD Command: {:#04x?}", val),
severity: Error,
stub_val: None,
});
}
fn read_data(&mut self) -> MemResult<u16> {
self.handle_data_read()
}

Ok(())
}
0x10 => Ok(self.handle_data_write(val)?),
_ => Err(Unexpected),
}
fn render_callback(&self) -> RenderCallback {
self.make_render_callback()
}
}
24 changes: 24 additions & 0 deletions clicky-core/src/devices/display/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
//! Display-related devices.

use crate::devices::prelude::*;

use crate::gui::RenderCallback;

pub mod hd66753;

/// LCD Controller IC trait (eg. HD66753)
pub trait LcdPanel: std::fmt::Debug + Send + Sync {
/// Select a register / issue a command (i.e: write the Index Register).
fn write_command(&mut self, val: u16) -> MemResult<()>;

/// Read back the command register.
fn read_command(&mut self) -> MemResult<u16>;

/// Write to the currently selected register.
fn write_data(&mut self, val: u16) -> MemResult<()>;

/// Read from the currently selected register.
fn read_data(&mut self) -> MemResult<u16>;

/// Returns a callback which renders the panel's framebuffer.
///
/// The callback accepts a framebuffer, and returns the rendered dimensions.
fn render_callback(&self) -> RenderCallback;
}
2 changes: 2 additions & 0 deletions clicky-core/src/devices/platform/pp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod i2s;
mod intcon;
mod mailbox;
mod memcon;
mod mlcd;
mod opto;
mod ppcon;
mod rtc;
Expand All @@ -40,6 +41,7 @@ pub use i2s::*;
pub use intcon::*;
pub use mailbox::*;
pub use memcon::*;
pub use mlcd::*;
pub use opto::*;
pub use ppcon::*;
pub use rtc::*;
Expand Down
100 changes: 100 additions & 0 deletions clicky-core/src/devices/platform/pp/mlcd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::devices::prelude::*;

use crate::devices::display::LcdPanel;
use crate::gui::RenderCallback;

/// PP5020 monochrome LCD controller.
///
/// The panel is driven over an 8-bit interface, so each 16-bit transfer takes
/// two accesses. Writes latch the high byte and commit on the second write;
/// reads return the high byte first and latch the low byte for the next read.
#[derive(Debug)]
pub struct MonoLcdBridge {
// FIXME: not sure if there are separate latches for the command and data
// registers...
write_byte_latch: Option<u8>,
read_byte_latch: Option<u8>,

panel: Box<dyn LcdPanel>,
}

impl MonoLcdBridge {
pub fn new(panel: Box<dyn LcdPanel>) -> MonoLcdBridge {
MonoLcdBridge {
write_byte_latch: None,
read_byte_latch: None,
panel,
}
}

/// Returns a callback to update the framebuffer.
pub fn render_callback(&self) -> RenderCallback {
self.panel.render_callback()
}
}

impl Device for MonoLcdBridge {
fn kind(&self) -> &'static str {
"Mono LCD Bridge"
}

fn probe(&self, offset: u32) -> Probe {
let reg = match offset {
0x0 => "LCD Control",
0x8 => "LCD Command",
0x10 => "LCD Data",
_ => return Probe::Unmapped,
};

Probe::Register(reg)
}
}

impl Memory for MonoLcdBridge {
fn r32(&mut self, offset: u32) -> MemResult<u32> {
if offset == 0x0 {
// bypass the latch
//
// Bit 15 is BUSY (iPodLinux: `lcd_busy_mask = 0x8000`), which
// guests poll before each transfer. HACK: the emulated bridge
// completes transfers instantly, so it is never busy.
return Ok(0);
}

if let Some(val) = self.read_byte_latch.take() {
return Ok(val as u32);
}

let val: u16 = match offset {
0x8 => self.panel.read_command()?,
0x10 => self.panel.read_data()?,
_ => return Err(Unexpected),
};

self.read_byte_latch = Some(val as u8); // latch lower 8 bits
Ok((val >> 8) as u32) // returning the higher 8 bits first
}

fn w32(&mut self, offset: u32, val: u32) -> MemResult<()> {
if offset == 0x0 {
// bypass the latch
return Err(StubWrite(Error, ()));
}

// the iPod uses the controller via an 8-bit interface
let val = val as u8; // FIXME: this should use trunc_to_u8, but it crashes...
let val = match self.write_byte_latch.take() {
None => {
self.write_byte_latch = Some(val);
return Ok(());
}
Some(hi) => (hi as u16) << 8 | (val as u16),
};

match offset {
0x8 => self.panel.write_command(val),
0x10 => self.panel.write_data(val),
_ => Err(Unexpected),
}
}
}
8 changes: 4 additions & 4 deletions clicky-core/src/sys/ipod4g/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@

/// Return the system's RenderCallback method.
pub fn render_callback(&self) -> RenderCallback {
self.devices.hd66753.render_callback()
self.devices.mlcd.render_callback()
}
}

Expand All @@ -367,7 +367,7 @@
pub cpuid: devices::CpuIdReg,
pub flash: devices::Flash,
pub cpucon: devices::CpuCon,
pub hd66753: devices::Hd66753,
pub mlcd: devices::MonoLcdBridge,
pub timer1: devices::CfgTimer,
pub timer2: devices::CfgTimer,
pub usec_timer: devices::UsecTimer,
Expand Down Expand Up @@ -467,7 +467,7 @@
usb: Usb::new(),
flash: Flash::new(),
cpucon: CpuCon::new(task_spawner.clone()),
hd66753: Hd66753::new(),
mlcd: MonoLcdBridge::new(Box::new(Hd66753::new())),
timer1: CfgTimer::new("1", timer1_irq_tx, task_spawner.clone()),
timer2: CfgTimer::new("2", timer2_irq_tx, task_spawner),
usec_timer: UsecTimer::new(),
Expand Down Expand Up @@ -519,7 +519,7 @@
fn $fn(&mut self, addr: u32) -> MemResult<$ret> {
let mut addr = addr;
if (0x00..0x1F).contains(&addr) && self.cachecon.local_evt {
addr = addr | 0x6000_f100;

Check warning on line 522 in clicky-core/src/sys/ipod4g/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

manual implementation of an assign operation

warning: manual implementation of an assign operation --> clicky-core/src/sys/ipod4g/mod.rs:522:25 | 522 | addr = addr | 0x6000_f100; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `addr |= 0x6000_f100` ... 615 | / mmap! { 616 | | RAM { 617 | | 0x1000_0000..=0x11ff_ffff => sdram, 618 | | 0x4000_0000..=0x4001_7fff => fastram, ... | 693 | | } | |_- in this macro invocation | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#assign_op_pattern = note: this warning originates in the macro `impl_mem_r` which comes from the expansion of the macro `mmap` (in Nightly builds, run with -Z macro-backtrace for more info)

Check warning on line 522 in clicky-core/src/sys/ipod4g/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

manual implementation of an assign operation

warning: manual implementation of an assign operation --> clicky-core/src/sys/ipod4g/mod.rs:522:25 | 522 | addr = addr | 0x6000_f100; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `addr |= 0x6000_f100` ... 615 | / mmap! { 616 | | RAM { 617 | | 0x1000_0000..=0x11ff_ffff => sdram, 618 | | 0x4000_0000..=0x4001_7fff => fastram, ... | 693 | | } | |_- in this macro invocation | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#assign_op_pattern = note: this warning originates in the macro `impl_mem_r` which comes from the expansion of the macro `mmap` (in Nightly builds, run with -Z macro-backtrace for more info)

Check warning on line 522 in clicky-core/src/sys/ipod4g/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

manual implementation of an assign operation

warning: manual implementation of an assign operation --> clicky-core/src/sys/ipod4g/mod.rs:522:25 | 522 | addr = addr | 0x6000_f100; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `addr |= 0x6000_f100` ... 615 | / mmap! { 616 | | RAM { 617 | | 0x1000_0000..=0x11ff_ffff => sdram, 618 | | 0x4000_0000..=0x4001_7fff => fastram, ... | 693 | | } | |_- in this macro invocation | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#assign_op_pattern = note: `#[warn(clippy::assign_op_pattern)]` on by default = note: this warning originates in the macro `impl_mem_r` which comes from the expansion of the macro `mmap` (in Nightly builds, run with -Z macro-backtrace for more info)
}

let (phys_addr, prot) = self.memcon.virt_to_phys(addr, MemAccessKind::Read);
Expand Down Expand Up @@ -643,7 +643,7 @@
0x6400_4000..=0x6400_41ff => intcon, // i guess there's a mirror?

0x7000_0000..=0x7000_1fff => ppcon,
0x7000_3000..=0x7000_301f => hd66753,
0x7000_3000..=0x7000_301f => mlcd,
0x7000_6000..=0x7000_603f => serial0,
0x7000_6040..=0x7000_607f => serial1,
0x7000_a000..=0x7000_a03f => pwmcon,
Expand Down
Loading