PS/2 Mouse Clarification

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

PS/2 Mouse Clarification

Post by rkennedy9064 »

I started playing around with PS/2 mouse input and was wondering if anyone could clarify a few things.

The first question has to do with discarding packets. The wiki says that you should discard the entire packet if the x and y overflow bits are set. Does this mean you ignore the packet and the next packet read will be considered the flags packet, or do I need to ignore the next 2 reads for it to be considered the next group?

My second questions has to do with the delta x and delta y bytes. Is the delta value just a general number for how many units it moved in a particular direction, or is it determined from the difference between the previous number and the current number?

My third question has to do with expected values for delta x and delta y. I've been doing some testing with small mouse movements and most of the readings seem to be expected. I'll get positive and negative deltas between 1-10 in both directions depending on which way I move. I've also noticed that randomly the delta x, or delta y will have really high values between 250-256 even though I barely moved the mouse. I've noticed this more when I switch directions quickly on an axis. It can go from a -5 x delta to a 255 x delta even though the mouse barely moved. Is this something anyone here has encountered a lot? I'm not sure if these values are expected or not.

Here's an example of what I'm describing in my third question since it might help clarify things.

Code: Select all

positive x: 8
positive y: 4
positive x: 1
positive y: 0
positive x: 0
positive y: 1
negative x: 255
positive y: 1
negative x: 24
positive y: 24
negative x: 24
positive y: 242
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: PS/2 Mouse Clarification

Post by alexfru »

https://wiki.osdev.org/Mouse_Input says
Maximum possible values are +255 to -256 (they are 9-bit quantities, two's complement).
Are you combining the 1+1+8+8 bits correctly and sign-extending the pair of 9-bit values to 16 or 32 bits?
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

Re: PS/2 Mouse Clarification

Post by rkennedy9064 »

alexfru wrote:https://wiki.osdev.org/Mouse_Input says
Maximum possible values are +255 to -256 (they are 9-bit quantities, two's complement).
Are you combining the 1+1+8+8 bits correctly and sign-extending the pair of 9-bit values to 16 or 32 bits?
I was curious about that also. Originally I used viewtopic.php?f=1&t=10247 as a starting point and the other examples I found all treat the x and y deltas as bytes, but the documentation in the link you posted says it's 9 bits. Does that mean I need to read a short from the port instead of a char?
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: PS/2 Mouse Clarification

Post by Octocontrabass »

rkennedy9064 wrote:Does that mean I need to read a short from the port instead of a char?
No. It means you should combine the upper bits of the X and Y values from the first byte of the mouse packet with the remaining 8 bits in the second and third bytes, and sign-extend the 9-bit results into shorts or ints or whichever signed integer type is most convenient for you.

Your current output looks like you're updating the mouse packet buffer in memory and displaying it partway through a packet, so the low 8 bits of the X and Y values belong to the previous packet.
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

Re: PS/2 Mouse Clarification

Post by rkennedy9064 »

Octocontrabass wrote:
rkennedy9064 wrote:Does that mean I need to read a short from the port instead of a char?
No. It means you should combine the upper bits of the X and Y values from the first byte of the mouse packet with the remaining 8 bits in the second and third bytes, and sign-extend the 9-bit results into shorts or ints or whichever signed integer type is most convenient for you.

Your current output looks like you're updating the mouse packet buffer in memory and displaying it partway through a packet, so the low 8 bits of the X and Y values belong to the previous packet.
Okay, so just to double check my calculations, let's say these are the values for my first 3 packets.

Code: Select all

packet 1 = 1110_1001 (0xE9)
packet 2 = 0011_1000 (0x38)
packet 3 = 1101_1001 (0xD9)
Since the x sign bit is 0 in the first packet, sign extending would just add 0's to packet 2 since it's even, giving me an x value of 56 (0x0038).

Since the y sign bit is 1 in the first packet, it gets extended with 1's to give me -39 (0xFFD9).

Is that math correct, or am I still not understanding this all the way?
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: PS/2 Mouse Clarification

Post by alexfru »

rkennedy9064 wrote: Since the x sign bit is 0 in the first packet, sign extending would just add 0's to packet 2 since it's even, giving me an x value of 56 (0x0038).

Since the y sign bit is 1 in the first packet, it gets extended with 1's to give me -39 (0xFFD9).
Right.
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

Re: PS/2 Mouse Clarification

Post by rkennedy9064 »

alexfru wrote:
rkennedy9064 wrote: Since the x sign bit is 0 in the first packet, sign extending would just add 0's to packet 2 since it's even, giving me an x value of 56 (0x0038).

Since the y sign bit is 1 in the first packet, it gets extended with 1's to give me -39 (0xFFD9).
Right.
Okay awesome, so I guess that only leaves me with my first two questions. When the overflow bit is set in the first packet and I need to discard it. Do I discard the first packet, then read and discard packet 2 and 3, or does the next packet I was going to read become the new data packet?

And as for the delta x and delta y packets, Is the correct way to determine how much movement occurred just to find the difference between the previous x value and the current x value, or do I use the x data packet directly?
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: PS/2 Mouse Clarification

Post by alexfru »

rkennedy9064 wrote:And as for the delta x and delta y packets, Is the correct way to determine how much movement occurred just to find the difference between the previous x value and the current x value, or do I use the x data packet directly?
The mouse does not maintain some kind of x and y coordinates. You do. It just tells you how much to move along each axis. So, you have your x and y variables somewhere and add/subtract to/from them what the mouse reports.
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

Re: PS/2 Mouse Clarification

Post by rkennedy9064 »

Thanks for the help so far. I seem to be getting more reliable x and y movements after sign extending properly. One thing I still notice is that I seem to be getting a few unreliable results that are still thrown in. If I move the mouse at a consistent rate everything seems correct, but if I add any sort of speedup I start to see values like this.
  • -3, -1, -1, -4, -1, -10, -255, -5, -6, -3,-3
I also see this happen on the x axis sometimes, but it occurs far less frequently then the y axis. Below is a copy of the code I'm using to process the packets. Does anything jump out as being incorrect?

Code: Select all

pub fn process_packet(&mut self, packet: u8) {
    match self.current_packet {
        0 => {
            let flags = MouseFlags::from_bits(packet).unwrap();
            if flags.contains(MouseFlags::X_OVERFLOW | MouseFlags::Y_OVERFLOW) {
                return;
            }
            self.data_packet = flags;
        }
        1 => self.process_x_movement(packet),
        2 => self.process_y_movement(packet),
        _ => unreachable!(),
    }
    self.current_packet = (self.current_packet + 1) % 3;
}

fn process_x_movement(&mut self, packet: u8) {
    match self.data_packet.contains(MouseFlags::X_SIGN) {
        true => self.x = self.sign_extend(packet),
        false => self.x = packet as i16,
    }
}

fn process_y_movement(&mut self, packet: u8) {
    match self.data_packet.contains(MouseFlags::Y_SIGN) {
        true => self.y = self.sign_extend(packet),
        false => self.y = packet as i16,
    }
}

fn sign_extend(&self, packet: u8) -> i16 {
    ((packet as u16) | 0xFF00) as i16
}
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: PS/2 Mouse Clarification

Post by alexfru »

rkennedy9064 wrote:I seem to be getting more reliable x and y movements after sign extending properly. One thing I still notice is that I seem to be getting a few unreliable results that are still thrown in. If I move the mouse at a consistent rate everything seems correct, but if I add any sort of speedup I start to see values like this.
  • -3, -1, -1, -4, -1, -10, -255, -5, -6, -3,-3
I also see this happen on the x axis sometimes, but it occurs far less frequently then the y axis. Below is a copy of the code I'm using to process the packets. Does anything jump out as being incorrect?

Code: Select all

pub fn process_packet(&mut self, packet: u8) {
    match self.current_packet {
        0 => {
            let flags = MouseFlags::from_bits(packet).unwrap();
            if flags.contains(MouseFlags::X_OVERFLOW | MouseFlags::Y_OVERFLOW) {
                return;
            }
            self.data_packet = flags;
        }
        1 => self.process_x_movement(packet),
        2 => self.process_y_movement(packet),
        _ => unreachable!(),
    }
    self.current_packet = (self.current_packet + 1) % 3;
}
I don't think a movement overflow changes the packet order. The two are (or should be) unrelated. I mean, if you see an overflow bit set, you should still advance your self.current_packet. If an overflow occurs, it will likely make your current code misinterpret the following byte packets.

You can simply ignore the movement along the axis for which there's an overflow condition, while properly continuously counting all packets.

However, if you ignore it, then this whole overflow thing becomes mostly useless since the overflow flag itself doesn't even tell you whether the overflow is for a negative or a positive movement. When an overflow is set, I'd think that the value should still be usable. Most likely it would just be capped/saturated to its possible min/max, that is, -256(or -255) or +255. It's also possible for the range to be from -128(or -127) to 127 as shown in this old thread: PS/2 mouse sign and overflow.

Again, when an overflow is set, it would be unhelpful to report, say, a movement of 100 instead of -256 or -100 instead of 255 (note the opposite direction, the kind of thing that really happens when numeric overflow goes undetected or unhandled).

This suggests that the sign bit and the rest eight bits should be usable even on overflow. I haven't seen this elaborated in any of PS/2 docs I have, but this is how I'd do it for the overflow mechanism to be useful.
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

Re: PS/2 Mouse Clarification

Post by rkennedy9064 »

alexfru wrote:
rkennedy9064 wrote:I seem to be getting more reliable x and y movements after sign extending properly. One thing I still notice is that I seem to be getting a few unreliable results that are still thrown in. If I move the mouse at a consistent rate everything seems correct, but if I add any sort of speedup I start to see values like this.
  • -3, -1, -1, -4, -1, -10, -255, -5, -6, -3,-3
I also see this happen on the x axis sometimes, but it occurs far less frequently then the y axis. Below is a copy of the code I'm using to process the packets. Does anything jump out as being incorrect?

Code: Select all

pub fn process_packet(&mut self, packet: u8) {
    match self.current_packet {
        0 => {
            let flags = MouseFlags::from_bits(packet).unwrap();
            if flags.contains(MouseFlags::X_OVERFLOW | MouseFlags::Y_OVERFLOW) {
                return;
            }
            self.data_packet = flags;
        }
        1 => self.process_x_movement(packet),
        2 => self.process_y_movement(packet),
        _ => unreachable!(),
    }
    self.current_packet = (self.current_packet + 1) % 3;
}
I don't think a movement overflow changes the packet order. The two are (or should be) unrelated. I mean, if you see an overflow bit set, you should still advance your self.current_packet. If an overflow occurs, it will likely make your current code misinterpret the following byte packets.

You can simply ignore the movement along the axis for which there's an overflow condition, while properly continuously counting all packets.

However, if you ignore it, then this whole overflow thing becomes mostly useless since the overflow flag itself doesn't even tell you whether the overflow is for a negative or a positive movement. When an overflow is set, I'd think that the value should still be usable. Most likely it would just be capped/saturated to its possible min/max, that is, -256(or -255) or +255. It's also possible for the range to be from -128(or -127) to 127 as shown in this old thread: PS/2 mouse sign and overflow.

Again, when an overflow is set, it would be unhelpful to report, say, a movement of 100 instead of -256 or -100 instead of 255 (note the opposite direction, the kind of thing that really happens when numeric overflow goes undetected or unhandled).

This suggests that the sign bit and the rest eight bits should be usable even on overflow. I haven't seen this elaborated in any of PS/2 docs I have, but this is how I'd do it for the overflow mechanism to be useful.
Thanks for the tips. I tried advancing self.current_packet like you suggested and just ignore the x and y packet if overflow is set and noticed something else that's strange. I started monitoring the flags packet and it seems like at some point, it starts returning 0xFF, even though I'm not pressing any buttons, then after a few reads of 0xFF, it starts returning values of either 1 or 0, so the always set bit stops getting set.

This makes me think something is changing the order of the packets? I queried the mouse id after initialization and it returns a mouse id of 0 and from what I understand that means there won't be an optional 4th packet. Could anything else effect the order of the packets? I've added the updated code used to get this result below. It now continues to process the packets, but only saves the x or y value if there's no overflow. That should process packets in groups of 3, so I'm not sure why the always set bit randomly stops getting set, but that seems like it could be the reason I'm getting unexpected values?

Code: Select all

pub fn process_packet(&mut self, packet: u8) {
    match self.current_packet {
        0 => self.data_packet = MouseFlags::from_bits(packet).unwrap(),
        1 => self.process_x_movement(packet),
        2 => self.process_y_movement(packet),
        _ => unreachable!(),
    }
    self.current_packet = (self.current_packet + 1) % 3;
}

fn process_x_movement(&mut self, packet: u8) {
    if !self.data_packet.contains(MouseFlags::X_OVERFLOW) {
        match self.data_packet.contains(MouseFlags::X_SIGN) {
            true => self.x = self.sign_extend(packet),
            false => self.x = packet as i16,
        }
    }
}

fn process_y_movement(&mut self, packet: u8) {
    if !self.data_packet.contains(MouseFlags::Y_OVERFLOW) {
        match self.data_packet.contains(MouseFlags::Y_SIGN) {
            true => self.y = self.sign_extend(packet),
            false => self.y = packet as i16,
        }
    }
}
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: PS/2 Mouse Clarification

Post by alexfru »

Your configuration or ISR or I/O can be screwy. Or you may have a race condition somewhere.

I'd remove all processing and just immediately write every received byte packet to the screen to its position (one of the three), perhaps in binary and see what's happening. If there's life of its own when you aren't touching anything, if your bytes go to wrong positions, if things go crazy in some other way, etc.
rkennedy9064
Member
Member
Posts: 36
Joined: Wed Sep 01, 2010 3:54 pm

Re: PS/2 Mouse Clarification

Post by rkennedy9064 »

Okay I did a bit of debugging and think I found some useful information, but I'm not sure what's causing it. I only see mouse interrupts when I move the mouse, so that part looks like it's working correctly at least. I tried doing some button clicks, then moving the mouse an eventually it seemed like my flags packet had switched positions and is now the second packet in the grouping of three. Since I took out the processing of the packets, all my testing was in my interrupt, which makes me think I'm doing something wrong there.

Currently every time an interrupt is fired, I read a byte from port 0x60, but now I'm wondering if I need to read 3 bytes at a time? I was assuming each time an interrupt was fired it meant that there was a byte ready to be consumed. This is the code I have for my interrupt handler. Does anything look off to you?

Code: Select all

extern "x86-interrupt" fn mouse_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
    let mut port = PortReadOnly::new(0x60);
    let packet: u8 = unsafe { port.read() };
    println!("{:08b}", packet);

    unsafe {
        PICS.lock()
            .notify_end_of_interrupt(InterruptIndex::Mouse.into());
    }
}

Code: Select all

// Clicking left button, then right button
00001001 00000000 00000000
00001000 00000000 00000000
00001010 00000000 00000000
00001000 00000000 00000000

// Some mouse movement
00001000 00000010 00000000
00001000 00000100 00000010
00001000 00000101 00000010
00001000 00001000 00000010

// The switch happens and it looks like the second byte is now the flags packet
00000001 00001000 00000100
00000010 00001000 00000001
00000010 00001000 00000000
00000001 00001000 00000001

// Pressing the left mouse button, then right mouse button to confirm
00000000 00001001 00000000
00000000 00001000 00000000
00000000 00001010 00000000
00000000 00001000 00000000
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: PS/2 Mouse Clarification

Post by alexfru »

rkennedy9064 wrote:Currently every time an interrupt is fired, I read a byte from port 0x60, but now I'm wondering if I need to read 3 bytes at a time?
Yes, all 3 (or 4 if there's a wheel).
rkennedy9064 wrote:I was assuming each time an interrupt was fired it meant that there was a byte ready to be consumed. This is the code I have for my interrupt handler. Does anything look off to you?

Code: Select all

extern "x86-interrupt" fn mouse_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
    let mut port = PortReadOnly::new(0x60);
    let packet: u8 = unsafe { port.read() };
    println!("{:08b}", packet);

    unsafe {
        PICS.lock()
            .notify_end_of_interrupt(InterruptIndex::Mouse.into());
    }
}
My reads wait for the data by polling port 0x64 before reading from port 0x60.

I can also see multiple implementations issuing command 0xAD before those three reads and 0xAE afterwards (some kind of synchronization/acknowledgement?; the writes also poll port 0x64). I can't find where these two commands are coming from. You probably shouldn't send them until you know they're needed.

Then, there's the standard EOI sent to the slave PIC and then to the master PIC to acknowledge IRQ12, but you probably already have this.

So, yeah, read all 3 bytes every time you enter the ISR and use(poll) port 0x64 to know when you can read/write a byte of data.
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: PS/2 Mouse Clarification

Post by Octocontrabass »

rkennedy9064 wrote:Currently every time an interrupt is fired, I read a byte from port 0x60, but now I'm wondering if I need to read 3 bytes at a time?
No, you should read only one byte at a time. The PS/2 controller is dumb and doesn't know how many bytes are in one packet.

The PS/2 controller is shared by the keyboard as well, so a mistake there could interfere with your mouse driver.
alexfru wrote:My reads wait for the data by polling port 0x64 before reading from port 0x60.
You don't need to do this. The IRQ already indicates that you may read one byte from port 0x60.
alexfru wrote:I can also see multiple implementations issuing command 0xAD before those three reads and 0xAE afterwards (some kind of synchronization/acknowledgement?; the writes also poll port 0x64). I can't find where these two commands are coming from. You probably shouldn't send them until you know they're needed.
This sounds like a mistake due to poor understanding of the PS/2 controller. Those commands disable and enable the keyboard port, presumably to ensure that all bytes are being received from the mouse and not the keyboard, but you can receive from both devices at the same time. You can tell which device sent the byte by which IRQ was raised.
alexfru wrote:So, yeah, read all 3 bytes every time you enter the ISR and use(poll) port 0x64 to know when you can read/write a byte of data.
No, you should read one byte each time without polling port 0x64.
Post Reply