Skip to content
Snippets Groups Projects
serial.rs 6.79 KiB
Newer Older
  • Learn to ignore specific revisions
  • pub trait SerialDevice {
    
        /// Sends a byte (u8) to the attached serial connection.
    
        fn send(&mut self) -> u8;
    
    
        /// Receives a byte (u8) from the attached serial connection,
        /// can be either another device or the host.
    
        fn receive(&mut self, byte: u8);
    
    
        /// Whether the serial device "driver" supports slave mode
        /// simulating an external clock source. Or if instead the
        /// clock should always be generated by the running device.
        fn allow_slave(&self) -> bool;
    
    
        /// Returns a short description of the serial device.
        fn description(&self) -> String;
    
    
        /// Returns a string describing the current state of the
        /// serial device. Could be used for debugging purposes.
        fn state(&self) -> String;
    
    pub struct Serial {
        data: u8,
        control: u8,
        shift_clock: bool,
        clock_speed: bool,
        transferring: bool,
    
        timer: i16,
        length: u16,
        bit_count: u8,
    
        byte_receive: u8,
        int_serial: bool,
        device: Box<dyn SerialDevice>,
    
    }
    
    impl Serial {
        pub fn new() -> Self {
            Self {
                data: 0x0,
                control: 0x0,
                shift_clock: false,
                clock_speed: false,
                transferring: false,
    
                timer: 0,
                length: 512,
                bit_count: 0,
    
                byte_send: 0x0,
    
                byte_receive: 0x0,
                int_serial: false,
    
                device: Box::<NullDevice>::default(),
    
            }
        }
    
        pub fn reset(&mut self) {
            self.data = 0x0;
            self.control = 0x0;
            self.shift_clock = false;
            self.clock_speed = false;
            self.transferring = false;
    
            self.timer = 0;
            self.length = 512;
            self.bit_count = 0;
    
            self.byte_send = 0x0;
    
            self.byte_receive = 0x0;
            self.int_serial = false;
    
        pub fn clock(&mut self, cycles: u16) {
    
            if !self.transferring {
                return;
            }
    
            self.timer = self.timer.saturating_sub(cycles as i16);
            if self.timer <= 0 {
    
                let bit = (self.byte_receive >> (7 - self.bit_count)) & 0x01;
    
                self.data = (self.data << 1) | bit;
    
                self.tick_transfer();
    
                self.timer = self.length as i16;
            }
        }
    
    
        pub fn read(&mut self, addr: u16) -> u8 {
    
            match addr {
                // 0xFF01 — SB: Serial transfer data
                0xff01 => self.data,
                // 0xFF02 — SC: Serial transfer control
                0xff02 =>
    
    João Magalhães's avatar
    João Magalhães committed
                {
    
                    #[allow(clippy::bool_to_int_with_if)]
    
                    (if self.shift_clock { 0x01 } else { 0x00 }
                        | if self.clock_speed { 0x02 } else { 0x00 }
                        | if self.transferring { 0x80 } else { 0x00 })
                }
                _ => {
    
                    warnln!("Reding from unknown Serial location 0x{:04x}", addr);
    
                    0xff
                }
            }
        }
    
        pub fn write(&mut self, addr: u16, value: u8) {
    
            match addr {
                // 0xFF01 — SB: Serial transfer data
                0xff01 => self.data = value,
                // 0xFF02 — SC: Serial transfer control
                0xff02 => {
    
                    self.shift_clock = value & 0x01 == 0x01;
                    self.clock_speed = value & 0x02 == 0x02;
                    self.transferring = value & 0x80 == 0x80;
    
                    // in case the clock is meant to be set by the attached device
                    // and the current Game Boy is meant to be running in slave mode
                    // then checks if the attached device "driver" allows clock set
                    // by external device and if not then ignores the transfer request
                    // by immediately disabling the transferring flag
                    if !self.shift_clock && !self.device.allow_slave() {
    
                        self.transferring = false;
                    }
    
                    // in case a transfer of byte has been requested and
    
                    // this is the then we need to start the transfer setup
    
                    if self.transferring {
    
                        // @TODO: if the GBC mode exists there should
                        // be special check logic here
                        //self.length = if self.gb.is_cgb() && self.clock_speed { 16 } else { 512 };
                        self.length = 512;
                        self.bit_count = 0;
                        self.timer = self.length as i16;
    
                        // executes the send and receive operation immediately
                        // this is considered an operational optimization with
    
                        // no real effect on the emulation (ex: no timing issues)
    
                        // then stores the byte to be sent to the device so that
                        // it's sent by the end of the send cycle
    
                        self.byte_receive = self.device.send();
    
                        self.byte_send = self.data;
    
                }
                _ => warnln!("Writing to unknown Serial location 0x{:04x}", addr),
            }
        }
    
    
        pub fn send(&self) -> bool {
    
            if self.shift_clock {
                true
            } else {
                self.data & 0x80 == 0x80
            }
        }
    
    
        pub fn receive(&mut self, bit: bool) {
    
            if !self.shift_clock {
    
                self.data = (self.data << 1) | bit as u8;
                self.tick_transfer();
            }
        }
    
        #[inline(always)]
        pub fn int_serial(&self) -> bool {
            self.int_serial
        }
    
        #[inline(always)]
        pub fn set_int_serial(&mut self, value: bool) {
            self.int_serial = value;
        }
    
        #[inline(always)]
        pub fn ack_serial(&mut self) {
            self.set_int_serial(false);
        }
    
        pub fn device(&self) -> &dyn SerialDevice {
            self.device.as_ref()
        }
    
        pub fn set_device(&mut self, device: Box<dyn SerialDevice>) {
            self.device = device;
        }
    
        fn tick_transfer(&mut self) {
            self.bit_count += 1;
            if self.bit_count == 8 {
                self.transferring = false;
                self.length = 0;
    
                self.bit_count = 0;
    
                // received the byte on the device as the
                // complete send operation has been performed
                self.device.receive(self.byte_send);
    
    
                // signals the interrupt for the serial
                // transfer completion, indicating that
                // a new byte is ready to be read
                self.int_serial = true;
    
            }
        }
    }
    
    impl Default for Serial {
        fn default() -> Self {
            Self::new()
        }
    }
    
    pub struct NullDevice {}
    
    
    impl NullDevice {
        pub fn new() -> Self {
    
        }
    }
    
    impl SerialDevice for NullDevice {
        fn send(&mut self) -> u8 {
            0xff
        }
    
    
        fn receive(&mut self, _: u8) {}
    
    
        fn allow_slave(&self) -> bool {
            false
        }
    
    
        fn description(&self) -> String {
            String::from("Null")
        }
    
    
        fn state(&self) -> String {
            String::from("")
        }
    
    
    impl Default for NullDevice {
        fn default() -> Self {
            Self::new()
        }
    }