1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! ## cursor_rs
//! Getter & Setter cursor position on VGA(0xB8000) in freestanding Rust.
//!
//! ## Example
//! ```rust
//! #![no_std]
//! extern crate vgainfo_rs;
//! use vgainfo_rs::*;
//! extern crate cursor_rs;
//!
//! fn reset_screen() {
//!     let buffer_ptr = LENGTH as *mut VgaCharType;
//!     let iter = (0..LENGTH).map(|i| unsafe { buffer_ptr.add(i) });
//!
//!     for ptr in iter {
//!         let value = unsafe { ptr.read_volatile() };
//!         unsafe { ptr.write_volatile(value & 0xff00) };
//!     }
//!     cursor_rs::set_cursor(0,0);
//! }
//! ```

#![no_std]

extern crate inout_port_rs;
extern crate vgainfo_rs;

#[derive(Copy, Clone)]
#[repr(C, packed)]
struct __LowHigh {
    l: u8,
    h: u8,
}

#[derive(Copy, Clone)]
union __Cursor {
    value: u16,
    lh: __LowHigh,
}

fn set_cursor(y: usize, x: usize) {
    let cursor = __Cursor {
        value: (y * vgainfo_rs::WIDTH + x) as u16,
    };
    unsafe {
        inout_port_rs::outb(0xe, 0x3d4);
        inout_port_rs::outb(cursor.lh.h, 0x3d5);
        inout_port_rs::outb(0xf, 0x3d4);
        inout_port_rs::outb(cursor.lh.l, 0x3d5);
    }
}

fn get_cursor() -> (usize, usize) {
    let low: u8;
    let high: u8;

    unsafe {
        inout_port_rs::outb(0xe, 0x3d4);
        high = inout_port_rs::inb(0x3d5);
        inout_port_rs::outb(0xf, 0x3d4);
        low = inout_port_rs::inb(0x3d5);
    }

    let cursor = __Cursor {
        lh: __LowHigh { l: low, h: high },
    };

    let value = unsafe { cursor.value } as usize;
    let y = value / vgainfo_rs::WIDTH;
    let x = value % vgainfo_rs::WIDTH;

    (y, x)
}

/// An empty struct for invoking the method.
pub struct VgaCursor {}

impl VgaCursor {
    /// Create an empty struct for invoking the method.
    #[inline]
    pub const fn new() -> Self {
        Self {}
    }
}

/// Trait for getter and setter interface
pub trait VgaCurSorHal {
    /// Set cursor at (y,x) coordinates
    fn set(&mut self, y: usize, x: usize);
    /// Get cursor (y,x) coordinates
    fn get(&self) -> (usize, usize);
}

impl VgaCurSorHal for VgaCursor {
    /// Set cursor at y,x position.
    /// This method doesn't check range.
    #[inline]
    fn set(&mut self, y: usize, x: usize) {
        set_cursor(y, x);
    }

    /// Get current cursor position
    #[inline]
    fn get(&self) -> (usize, usize) {
        get_cursor()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn __cursor_works() {
        let lh = __LowHigh { l: 1, h: 1 };
        let cursor = __Cursor { lh: lh };
        assert_eq!(unsafe { cursor.value }, (1u16 << 8) | (1u16 << 0));
    }

    #[test]
    fn __usize_as_u16_works() {
        let width = vgainfo_rs::WIDTH;
        let high = vgainfo_rs::HIGH;

        let width_u16 = width as u16;
        let high_u16 = high as u16;

        assert_eq!((width, high), (width_u16 as usize, high_u16 as usize));
    }
}