Page 1 of 1

Why does PS/2 Mouse Driver is not working in real machine?

Posted: Tue Sep 16, 2025 7:48 am
by gamingjam60
Here My PS/2 Mouse Driver code which is working in QEmu and VirtualBox but pointer is not showinng in a real machine.

Code: Select all

/ Mouse cursor dimensions and colors
#define CURSOR_WIDTH 12
#define CURSOR_HEIGHT 12
#define CURSOR_COLOR COLOR_RED      // Red
#define CURSOR_BG_COLOR COLOR_BLACK // Black (to erase old position)

#define MOUSE_IRQ 12       // IRQ number for PS/2 mouse
#define MOUSE_VECTOR 44    // APIC interrupt vector for PS/2 mouse

#define MOUSE_CMD_PORT 0x64
#define MOUSE_DATA_PORT 0x60

extern uint64_t fb0_width;
extern uint64_t fb0_height;


volatile int mouse_x = 40; // Initial cursor x position
volatile int mouse_y = 12; // Initial cursor y position

volatile bool mouse_left_pressed = false;
volatile bool mouse_right_pressed = false;
volatile bool mouse_middle_pressed = false;


uint8_t mouse_cycle = 0;
uint8_t mouse_bytes[3];      // Signed byte
bool mouse_initialized = false;

// Buffer to store the background under the cursor
static uint32_t cursor_background[CURSOR_WIDTH * CURSOR_HEIGHT];

// Save the background pixels under the cursor position
void save_cursor_background(int x, int y) {
    for (int i = 0; i < CURSOR_HEIGHT; i++) {
        for (int j = 0; j < CURSOR_WIDTH; j++) {
            int px = x + j;
            int py = y + i;
            
            if (px >= 0 && px < (int)fb0_width && py >= 0 && py < (int)fb0_height) {
                cursor_background[i * CURSOR_WIDTH + j] = get_pixel(px, py);
            } else {
                cursor_background[i * CURSOR_WIDTH + j] = CURSOR_BG_COLOR;
            }
        }
    }
}

// Restore the background pixels at the cursor position
void restore_cursor_background(int x, int y) {
    for (int i = 0; i < CURSOR_HEIGHT; i++) {
        for (int j = 0; j < CURSOR_WIDTH; j++) {
            int px = x + j;
            int py = y + i;
            
            if (px >= 0 && px < (int)fb0_width && py >= 0 && py < (int)fb0_height) {
                set_pixel(px, py, cursor_background[i * CURSOR_WIDTH + j]);
            }
        }
    }
}

void mouse_wait(int type) {
    int timeout = 100000; // Timeout for waiting
    if (type == 0) {
        while (--timeout && (inb(MOUSE_CMD_PORT) & 1) == 0);
    } else {
        while (--timeout && (inb(MOUSE_CMD_PORT) & 2) == 0);
    }
}



void mouse_write(uint8_t data) {
    mouse_wait(1);
    outb(MOUSE_CMD_PORT, 0xD4);
    mouse_wait(1);
    outb(MOUSE_DATA_PORT, data);
}

uint8_t mouse_read() {
    mouse_wait(0);
    return inb(MOUSE_DATA_PORT);
}




// Draws the mouse cursor at its current position.
void draw_mouse_cursor(int mouse_x, int mouse_y, uint32_t color) {
    // Save the current background before drawing the cursor
    save_cursor_background(mouse_x, mouse_y);
    
    // Draw the cursor (simple rectangle for now)
    for (int i = 0; i < CURSOR_HEIGHT; i++) {
        for (int j = 0; j < CURSOR_WIDTH; j++) {
            int px = mouse_x + j;
            int py = mouse_y + i;
            
            if (px >= 0 && px < (int)fb0_width && py >= 0 && py < (int)fb0_height) {
                set_pixel(px, py, color);
            }
        }
    }
}


// Erases the mouse cursor at the given old position.
void erase_mouse_cursor(int old_x, int old_y) {
    // Restore the background that was behind the cursor
    restore_cursor_background(old_x, old_y);
}



// Process incoming mouse data packets.
static void mouse_handler() {

    int old_x = mouse_x;
    int old_y = mouse_y;

    uint8_t data = mouse_read();
    
    // Sync check and packet parsing
    if (mouse_cycle == 0) {
        if (!(data & 0x08)) return; // Wait for valid first byte
        mouse_bytes[0] = data;
        mouse_cycle++;
    } else if (mouse_cycle == 1) {
        mouse_bytes[1] = data;
        mouse_cycle++;
    } else if (mouse_cycle == 2) {
        mouse_bytes[2] = data;
        mouse_cycle = 0;

        uint8_t status = mouse_bytes[0];
        // Correct sign extension for 9-bit two's complement
        int dx = (int)((status & 0x10) ? (mouse_bytes[1] | 0xFFFFFF00) : mouse_bytes[1]);
        int dy = (int)((status & 0x20) ? (mouse_bytes[2] | 0xFFFFFF00) : mouse_bytes[2]);

        // Check overflow bits
        if (status & 0x40 || status & 0x80) {
            return; // discard this packet
        }

        // Update mouse position
        erase_mouse_cursor(old_x, old_y);
        mouse_x += dx;
        mouse_y -= dy; // Invert Y axis to match screen coordinates

        // Clamp to screen boundaries
        if (mouse_x < 0) mouse_x = 0;
        if (mouse_y < 0) mouse_y = 0;
        if (mouse_x >= (int) fb0_width - CURSOR_WIDTH) 
            mouse_x = (int) fb0_width - CURSOR_WIDTH;
        if (mouse_y >= (int) fb0_height - CURSOR_HEIGHT) 
            mouse_y = (int) fb0_height - CURSOR_HEIGHT;

        draw_mouse_cursor(mouse_x, mouse_y, CURSOR_COLOR);
        mouse_left_pressed = (status & 0x01);
    }

    // Update button
    mouse_left_pressed = (mouse_bytes[0] & 0x01) ? true : false;
    mouse_right_pressed = (mouse_bytes[0] & 0x02) ? true : false;
    mouse_middle_pressed = (mouse_bytes[0] & 0x04) ? true : false;
}


// This is the interrupt handler called by the APIC.
// It processes the mouse packet and signals the end of interrupt.
void mouseHandler(registers_t *regs) {
    mouse_handler();
    apic_send_eoi();  // Signal end-of-interrupt for APIC
}

// Enable mouse interrupts using APIC (no PIC modifications needed)
void enable_mouse() {
    irq_install(MOUSE_IRQ, &mouseHandler);
}

// Disable mouse interrupts
void disable_mouse() {
    irq_uninstall(MOUSE_VECTOR);
}


// Install and initialize the PS/2 mouse device.
void mouse_install() {

    mouse_wait(1);
    outb(MOUSE_CMD_PORT, 0xA8);  // Enable auxiliary device (mouse)

    mouse_wait(1);
    outb(MOUSE_CMD_PORT, 0x20);  // Get Compaq status byte

    mouse_wait(0);
    uint8_t status = mouse_read() | 2;
    mouse_wait(1);
    outb(MOUSE_CMD_PORT, 0x60);  // Set Compaq status byte

    mouse_wait(1);
    outb(MOUSE_DATA_PORT, status);

    mouse_write(0xF6);           // Set default settings
    mouse_read();                // Acknowledge
    
    mouse_write(0xF4);           // Enable packet streaming
    mouse_read();                // Acknowledge

    enable_mouse();             // Enable mouse interrupts via APIC

    mouse_initialized = true;
}


void mouse_init() {
    asm volatile("cli");  // Disable interrupts
    
    uint32_t flag = IOAPIC_EDGE_TRIG | IOAPIC_HIGH_ACTIVE | IOAPIC_FIXED | IOAPIC_UNMASKED;
    ioapic_route_irq(MOUSE_IRQ, 0, MOUSE_VECTOR, flag );    // For bootstrap cpu

    mouse_install();

    asm volatile("sti");    // Enable interrupts

    printf("[Info] Successfully MOUSE initialized.\n");
}


Repo

Re: Why does PS/2 Mouse Driver is not working in real machine?

Posted: Tue Sep 16, 2025 10:15 am
by Octocontrabass
What debugging have you tried so far?

Re: Why does PS/2 Mouse Driver is not working in real machine?

Posted: Tue Sep 16, 2025 12:08 pm
by BenLunt
Could it be that your code works just fine, however the set_pixel() function isn't drawing to the screen as expected? Since you (presumably) aren't seeing anything drawn to the screen, maybe it isn't your mouse code, but your video display code. You don't say if other stuff is displayed or not, only that the pointer isn't shown.

To eliminate this scenario, temporarily place a call to draw_mouse_cursor(100, 100, GUICOLOR_White) just to make sure the cursor can actually be written to the screen.

Also, for future reference, if set_pixel() is drawing directly to the screen, by saving the area where the cursor will be, and then later restoring the area only works if the area behind the cursor never changes. If the cursor hovers over something that is or will change, when you restore the area, it will restore to what it was before, not to what it should be now.

Ben

Re: Why does PS/2 Mouse Driver is not working in real machine?

Posted: Wed Sep 17, 2025 2:34 pm
by BenLunt
I'm just curious to see if you could determine if it was the screen drawing portion or the mouse driver portion of your code. You didn't state, but are you able to draw other items to the screen on the real hardware?

If it is your drawing routine, are you drawing to a VESA define Frame Buffer, or are you drawing to the VGA defined buffer at 0xA0000? If it is the latter, and you tested on recent real hardware, it is a good possibility the recent real hardware no longer has the physical legacy hardware of the VGA. i.e.: writing to 0xA0000 will do nothing as far as the screen is concerned.

Since all you stated was "is not working in real machine", we don't really know where to help you. What is not working? You don't see any graphical mouse cursor drawn, but other stuff is? You don't ever get an interrupt? (which is easy to test for with a properly placed printf() assuming you can print to the screen)

People aren't too willing to help out if you don't first try to debug yourself. Then when that fails, you can now explain what you have tried and what is not working, hence Octocontrabass' reply.

Re: Why does PS/2 Mouse Driver is not working in real machine?

Posted: Mon Sep 29, 2025 3:49 pm
by InvalidOpcode
Is the real machine definitely using a ps/2 mouse? (or for example a usb mouse with ps/2 emulation)

Have you checked the if the specific hardware/controllers/bios on the real machine have any known bugs?