Skip to content
Snippets Groups Projects
Verified Commit 561fb8db authored by João Magalhães's avatar João Magalhães :rocket:
Browse files

chore: initial printer device logic done

The image data buffer printing is still pending.
Still a lot of TODOs and edge cases to be solved.
parent f4af0704
No related branches found
No related tags found
1 merge request!23Support for serial data transfer 🔌
Pipeline #2503 failed
...@@ -444,7 +444,7 @@ fn main() { ...@@ -444,7 +444,7 @@ fn main() {
// creates a new Game Boy instance and loads both the boot ROM // creates a new Game Boy instance and loads both the boot ROM
// and the initial game ROM to "start the engine" // and the initial game ROM to "start the engine"
let mut game_boy = GameBoy::new(); let mut game_boy = GameBoy::new();
game_boy.attach_stdout_serial(); game_boy.attach_printer_serial();
game_boy.load_boot_default(); game_boy.load_boot_default();
// creates a new generic emulator structure then starts // creates a new generic emulator structure then starts
......
use crate::serial::SerialDevice; use std::fmt::{self, Display, Formatter};
pub struct PrinterDevice {} use crate::{serial::SerialDevice, warnln};
#[derive(Clone, Copy, PartialEq, Eq)]
enum PrinterState {
MagicBytes1 = 0x00,
MagicBytes2 = 0x01,
Identification = 0x02,
Compression = 0x03,
LengthLow = 0x04,
LengthHigh = 0x05,
Data = 0x06,
ChecksumLow = 0x07,
ChecksumHigh = 0x08,
KeepAlive = 0x09,
Status = 0x0a,
Other = 0xff,
}
impl PrinterState {
pub fn description(&self) -> &'static str {
match self {
PrinterState::MagicBytes1 => "Magic Bytes 1",
PrinterState::MagicBytes2 => "Magic Bytes 2",
PrinterState::Identification => "Identification",
PrinterState::Compression => "Compression",
PrinterState::LengthLow => "Length Low",
PrinterState::LengthHigh => "Length High",
PrinterState::Data => "Data",
PrinterState::ChecksumLow => "Checksum Low",
PrinterState::ChecksumHigh => "Checksum High",
PrinterState::KeepAlive => "Keep Alive",
PrinterState::Status => "Status",
PrinterState::Other => "Other",
}
}
fn from_u8(value: u8) -> Self {
match value {
0x00 => PrinterState::MagicBytes1,
0x01 => PrinterState::MagicBytes2,
0x02 => PrinterState::Identification,
0x03 => PrinterState::Compression,
0x04 => PrinterState::LengthLow,
0x05 => PrinterState::LengthHigh,
0x06 => PrinterState::Data,
0x07 => PrinterState::ChecksumLow,
0x08 => PrinterState::ChecksumHigh,
0x09 => PrinterState::KeepAlive,
0x0a => PrinterState::Status,
_ => PrinterState::Other,
}
}
}
impl Display for PrinterState {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.description())
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum PrinterCommand {
Init = 0x01,
Start = 0x02,
Data = 0x04,
Status = 0x0f,
Other = 0xff,
}
impl PrinterCommand {
pub fn description(&self) -> &'static str {
match self {
PrinterCommand::Init => "Init",
PrinterCommand::Start => "Start",
PrinterCommand::Data => "Data",
PrinterCommand::Status => "Status",
PrinterCommand::Other => "Other",
}
}
fn from_u8(value: u8) -> Self {
match value {
0x01 => PrinterCommand::Init,
0x02 => PrinterCommand::Start,
0x04 => PrinterCommand::Data,
0x0f => PrinterCommand::Status,
_ => PrinterCommand::Other,
}
}
}
impl Display for PrinterCommand {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.description())
}
}
pub struct PrinterDevice {
state: PrinterState,
command: PrinterCommand,
compression: bool,
command_length: u16,
length_left: u16,
checksum: u16,
status: u8,
byte_out: u8,
data: [u8; 0x280],
}
impl PrinterDevice { impl PrinterDevice {
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self {
state: PrinterState::MagicBytes1,
command: PrinterCommand::Other,
compression: false,
command_length: 0,
length_left: 0,
checksum: 0x0,
status: 0x0,
byte_out: 0x0,
data: [0x00; 0x280],
}
} }
}
impl Default for PrinterDevice { pub fn reset(&mut self) {
fn default() -> Self { self.state = PrinterState::MagicBytes1;
Self::new() self.command = PrinterCommand::Other;
self.compression = false;
self.command_length = 0;
self.length_left = 0;
self.checksum = 0x0;
self.status = 0x0;
self.byte_out = 0x0;
self.data = [0x00; 0x280]
}
fn run_command(&mut self, command: PrinterCommand) {
match command {
PrinterCommand::Init => {
self.status = 0x00;
self.byte_out = self.status;
}
PrinterCommand::Start => {
self.byte_out = self.status;
self.status = 0x06;
}
PrinterCommand::Data => {
if self.command_length == 0x280 {
println!("Printer: Going to copy the image for printing");
}
// in case the command is of size 0 we assume this is
// an EOF and we ignore this data operation
else if self.command_length == 0x0 {
} else {
warnln!(
"Printer: Wrong size for data: {:04x} bytes",
self.command_length
);
}
self.status = 0x08;
self.byte_out = self.status;
}
PrinterCommand::Status => {
self.byte_out = self.status;
// in case the current status is printing let's
// mark it as done
if self.status == 0x06 {
// @TODO: check if this value should be 0x04 instead
// this seems to be a bug with the print demo
self.status = 0x00;
}
}
PrinterCommand::Other => {
warnln!("Printer: Invalid command: {:02x}", self.state as u8);
return;
}
}
} }
} }
impl SerialDevice for PrinterDevice { impl SerialDevice for PrinterDevice {
fn send(&mut self) -> u8 { fn send(&mut self) -> u8 {
0xff self.byte_out
} }
fn receive(&mut self, byte: u8) { fn receive(&mut self, byte: u8) {
print!("{}", byte as char); self.byte_out = 0x00;
// @TODO: implement this one
match self.state {
PrinterState::MagicBytes1 => {
if byte != 0x88 {
warnln!("Printer: Invalid magic byte 1: {:02x}", byte);
return;
}
self.command = PrinterCommand::Other;
self.command_length = 0;
}
PrinterState::MagicBytes2 => {
if byte != 0x33 {
if byte != 0x88 {
self.state = PrinterState::MagicBytes1;
}
warnln!("Printer: Invalid magic byte 2: {:02x}", byte);
return;
}
}
PrinterState::Identification => self.command = PrinterCommand::from_u8(byte),
PrinterState::Compression => {
self.compression = byte & 0x01 == 0x01;
if self.compression {
warnln!("Printer: Using compressed data, currently unsupported");
}
}
PrinterState::LengthLow => self.length_left = byte as u16,
PrinterState::LengthHigh => self.length_left |= (byte as u16) << 8,
PrinterState::Data => {
self.data[self.command_length as usize] = byte;
self.command_length += 1;
self.length_left -= 1;
}
PrinterState::ChecksumLow => self.checksum = byte as u16,
PrinterState::ChecksumHigh => {
self.checksum |= (byte as u16) << 8;
self.byte_out = 0x81;
}
PrinterState::KeepAlive => {
self.run_command(self.command);
}
PrinterState::Status => {
self.state = PrinterState::MagicBytes1;
return;
}
PrinterState::Other => {
warnln!("Printer: Invalid state: {:02x}", self.state as u8);
return;
}
}
if self.state != PrinterState::Data {
self.state = PrinterState::from_u8(self.state as u8 + 1);
}
if self.state == PrinterState::Data && self.length_left == 0 {
self.state = PrinterState::from_u8(self.state as u8 + 1);
}
}
}
impl Default for PrinterDevice {
fn default() -> Self {
Self::new()
} }
} }
...@@ -12,12 +12,6 @@ impl StdoutDevice { ...@@ -12,12 +12,6 @@ impl StdoutDevice {
} }
} }
impl Default for StdoutDevice {
fn default() -> Self {
Self::new(true)
}
}
impl SerialDevice for StdoutDevice { impl SerialDevice for StdoutDevice {
fn send(&mut self) -> u8 { fn send(&mut self) -> u8 {
0xff 0xff
...@@ -30,3 +24,9 @@ impl SerialDevice for StdoutDevice { ...@@ -30,3 +24,9 @@ impl SerialDevice for StdoutDevice {
} }
} }
} }
impl Default for StdoutDevice {
fn default() -> Self {
Self::new(true)
}
}
...@@ -51,17 +51,13 @@ impl Serial { ...@@ -51,17 +51,13 @@ impl Serial {
} }
pub fn clock(&mut self, cycles: u8) { pub fn clock(&mut self, cycles: u8) {
if self.shift_clock {
return;
}
if !self.transferring { if !self.transferring {
return; return;
} }
self.timer = self.timer.saturating_sub(cycles as i16); self.timer = self.timer.saturating_sub(cycles as i16);
if self.timer <= 0 { if self.timer <= 0 {
let bit = self.byte_receive & (0x01 << self.bit_count); let bit = (self.byte_receive >> (7 - self.bit_count)) & 0x01;
self.data = (self.data << 1) | bit; self.data = (self.data << 1) | bit;
self.tick_transfer(); self.tick_transfer();
...@@ -93,11 +89,15 @@ impl Serial { ...@@ -93,11 +89,15 @@ impl Serial {
self.clock_speed = value & 0x02 == 0x02; self.clock_speed = value & 0x02 == 0x02;
self.transferring = value & 0x80 == 0x80; self.transferring = value & 0x80 == 0x80;
// @TODO: THIS SEEMS LIKE A HACK, we'll need to check with
// the device driver how to handle no communication and receive
// we must simulate no cable communication
if self.transferring && !self.shift_clock {
self.transferring = false;
}
// in case a transfer of byte has been requested and // in case a transfer of byte has been requested and
// this is the device responsible for the shifting // this is the then we need to start the transfer setup
// of the transfer's clock then we need to start else if self.transferring {
// the transfer setup
if self.transferring && self.shift_clock {
// @TODO: if the GBC mode exists there should // @TODO: if the GBC mode exists there should
// be special check logic here // be special check logic here
//self.length = if self.gb.is_cgb() && self.clock_speed { 16 } else { 512 }; //self.length = if self.gb.is_cgb() && self.clock_speed { 16 } else { 512 };
...@@ -183,12 +183,6 @@ impl NullDevice { ...@@ -183,12 +183,6 @@ impl NullDevice {
} }
} }
impl Default for NullDevice {
fn default() -> Self {
Self::new()
}
}
impl SerialDevice for NullDevice { impl SerialDevice for NullDevice {
fn send(&mut self) -> u8 { fn send(&mut self) -> u8 {
0xff 0xff
...@@ -196,3 +190,9 @@ impl SerialDevice for NullDevice { ...@@ -196,3 +190,9 @@ impl SerialDevice for NullDevice {
fn receive(&mut self, _: u8) {} fn receive(&mut self, _: u8) {}
} }
impl Default for NullDevice {
fn default() -> Self {
Self::new()
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment