github.com/icexin/eggos@v0.4.2-0.20220216025428-78b167e4f349/drivers/kbd/kbd.go (about)

     1  package kbd
     2  
     3  import (
     4  	"github.com/icexin/eggos/drivers/pic"
     5  	"github.com/icexin/eggos/kernel/sys"
     6  	"github.com/icexin/eggos/kernel/trap"
     7  )
     8  
     9  const (
    10  	_IRQ_KBD = pic.IRQ_BASE + pic.LINE_KBD
    11  )
    12  
    13  const (
    14  	KBSTATP = 0x64 // kbd controller status port(I)
    15  	KBS_DIB = 0x01 // kbd data in buffer
    16  	KBDATAP = 0x60 // kbd data port(I)
    17  
    18  	NO = 0
    19  
    20  	SHIFT = (1 << 0)
    21  	CTL   = (1 << 1)
    22  	ALT   = (1 << 2)
    23  
    24  	CAPSLOCK   = (1 << 3)
    25  	NUMLOCK    = (1 << 4)
    26  	SCROLLLOCK = (1 << 5)
    27  
    28  	E0ESC = (1 << 6)
    29  
    30  	// Special keycodes
    31  	KEY_HOME = 0xE0
    32  	KEY_END  = 0xE1
    33  	KEY_UP   = 0xE2
    34  	KEY_DN   = 0xE3
    35  	KEY_LF   = 0xE4
    36  	KEY_RT   = 0xE5
    37  	KEY_PGUP = 0xE6
    38  	KEY_PGDN = 0xE7
    39  	KEY_INS  = 0xE8
    40  	KEY_DEL  = 0xE9
    41  )
    42  
    43  var (
    44  	inputCallback func(byte)
    45  	keyPressed    [255]bool
    46  )
    47  
    48  func ctrl(c byte) byte {
    49  	return c - '@'
    50  }
    51  
    52  var shiftcode = [256]byte{
    53  	0x1d: CTL,
    54  	0x2A: SHIFT,
    55  	0x36: SHIFT,
    56  	0x38: ALT,
    57  	0x9d: CTL,
    58  	0xB8: ALT,
    59  }
    60  
    61  var togglecode = [256]byte{
    62  	0x3a: CAPSLOCK,
    63  	0x45: NUMLOCK,
    64  	0x46: SCROLLLOCK,
    65  }
    66  
    67  var normalmap = [256]byte{
    68  	NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
    69  	'7', '8', '9', '0', '-', '=', '\b', '\t',
    70  	'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
    71  	'o', 'p', '[', ']', '\n', NO, 'a', 's',
    72  	'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
    73  	'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
    74  	'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
    75  	NO, ' ', NO, NO, NO, NO, NO, NO,
    76  	NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
    77  	'8', '9', '-', '4', '5', '6', '+', '1',
    78  	'2', '3', '0', '.', NO, NO, NO, NO, // 0x50,
    79  	0x9c: '\n', // KP_Enter
    80  	0xB5: '/',  // KP_Div
    81  	0xc8: KEY_UP, 0xd0: KEY_DN,
    82  	0xC9: KEY_PGUP, 0xD1: KEY_PGDN,
    83  	0xCB: KEY_LF, 0xCD: KEY_RT,
    84  	0x97: KEY_HOME, 0xCF: KEY_END,
    85  	0xD2: KEY_INS, 0xD3: KEY_DEL,
    86  }
    87  
    88  var shiftmap = [256]byte{
    89  	NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
    90  	'&', '*', '(', ')', '_', '+', '\b', '\t',
    91  	'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
    92  	'O', 'P', '{', '}', '\n', NO, 'A', 'S',
    93  	'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
    94  	'"', '~', NO, '|', 'Z', 'X', 'C', 'V',
    95  	'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
    96  	NO, ' ', NO, NO, NO, NO, NO, NO,
    97  	NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
    98  	'8', '9', '-', '4', '5', '6', '+', '1',
    99  	'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
   100  	0x9C: '\n', // KP_Enter
   101  	0xB5: '/',  // KP_Div
   102  	0xC8: KEY_UP, 0xD0: KEY_DN,
   103  	0xC9: KEY_PGUP, 0xD1: KEY_PGDN,
   104  	0xCB: KEY_LF, 0xCD: KEY_RT,
   105  	0x97: KEY_HOME, 0xCF: KEY_END,
   106  	0xD2: KEY_INS, 0xD3: KEY_DEL,
   107  }
   108  
   109  var ctlmap = [256]byte{
   110  	NO, NO, NO, NO, NO, NO, NO, NO,
   111  	NO, NO, NO, NO, NO, NO, NO, NO,
   112  	ctrl('Q'), ctrl('W'), ctrl('E'), ctrl('R'), ctrl('T'), ctrl('Y'), ctrl('U'), ctrl('I'),
   113  	ctrl('O'), ctrl('P'), NO, NO, '\r', NO, ctrl('A'), ctrl('S'),
   114  	ctrl('D'), ctrl('F'), ctrl('G'), ctrl('H'), ctrl('J'), ctrl('K'), ctrl('L'), NO,
   115  	NO, NO, NO, ctrl('\\'), ctrl('Z'), ctrl('X'), ctrl('C'), ctrl('V'),
   116  	ctrl('B'), ctrl('N'), ctrl('M'), NO, NO, ctrl('/'), NO, NO,
   117  	0x9C: '\r',      // KP_Enter
   118  	0xB5: ctrl('/'), // KP_Div
   119  	0xC8: KEY_UP, 0xD0: KEY_DN,
   120  	0xC9: KEY_PGUP, 0xD1: KEY_PGDN,
   121  	0xCB: KEY_LF, 0xCD: KEY_RT,
   122  	0x97: KEY_HOME, 0xCF: KEY_END,
   123  	0xD2: KEY_INS, 0xD3: KEY_DEL,
   124  }
   125  
   126  var (
   127  	shift    byte
   128  	charcode = [...][256]byte{
   129  		normalmap, shiftmap, ctlmap, ctlmap,
   130  	}
   131  )
   132  
   133  //go:nosplit
   134  func ReadByte() int {
   135  	var (
   136  		st      byte
   137  		data, c byte
   138  	)
   139  	st = sys.Inb(KBSTATP)
   140  	if st&KBS_DIB == 0 {
   141  		return -1
   142  	}
   143  	// from mouse?
   144  	if st&0x20 != 0 {
   145  		return -1
   146  	}
   147  	data = byte(sys.Inb(KBDATAP))
   148  
   149  	switch {
   150  	case data == 0xE0:
   151  		shift |= E0ESC
   152  		return 0
   153  	case data&0x80 != 0:
   154  		// Key released
   155  		if shift&E0ESC == 0 {
   156  			data &= 0x7f
   157  		}
   158  		shift &= ^(shiftcode[data] | E0ESC)
   159  		c = charcode[shift&(CTL|SHIFT)][data]
   160  		keyPressed[c] = false
   161  		return 0
   162  	case shift&E0ESC != 0:
   163  		data |= 0x80
   164  		shift &= ^byte(E0ESC)
   165  	}
   166  
   167  	shift |= shiftcode[data]
   168  	shift ^= togglecode[data]
   169  	c = charcode[shift&(CTL|SHIFT)][data]
   170  	if shift&CAPSLOCK != 0 {
   171  		if 'a' <= c && c <= 'z' {
   172  			c -= 'a' - 'A'
   173  		} else if 'A' <= c && c <= 'Z' {
   174  			c += 'a' - 'A'
   175  		}
   176  	}
   177  	keyPressed[c] = true
   178  	return int(c)
   179  }
   180  
   181  const (
   182  	csiUp    = "\x1b[A"
   183  	csiDown  = "\x1b[B"
   184  	csiLeft  = "\x1b[D"
   185  	csiRight = "\x1b[C"
   186  )
   187  
   188  func csiEscape(ch byte) string {
   189  	switch ch {
   190  	case KEY_UP:
   191  		return csiUp
   192  	case KEY_DN:
   193  		return csiDown
   194  	case KEY_LF:
   195  		return csiLeft
   196  	case KEY_RT:
   197  		return csiRight
   198  	default:
   199  		return ""
   200  	}
   201  }
   202  
   203  //go:nosplit
   204  func intr() {
   205  	if inputCallback == nil {
   206  		return
   207  	}
   208  	for {
   209  		ch := ReadByte()
   210  		if ch <= 0 {
   211  			break
   212  		}
   213  		esc := csiEscape(byte(ch))
   214  		if esc == "" {
   215  			inputCallback(byte(ch))
   216  		} else {
   217  			for i := range esc {
   218  				inputCallback(esc[i])
   219  			}
   220  		}
   221  	}
   222  	pic.EOI(_IRQ_KBD)
   223  }
   224  
   225  func OnInput(callback func(byte)) {
   226  	inputCallback = callback
   227  }
   228  
   229  func Pressed(key byte) bool {
   230  	return keyPressed[key]
   231  }
   232  
   233  func Init() {
   234  	trap.Register(_IRQ_KBD, intr)
   235  	pic.EnableIRQ(pic.LINE_KBD)
   236  }