OpenCores
URL https://opencores.org/ocsvn/s80186/s80186/trunk

Subversion Repositories s80186

[/] [s80186/] [trunk/] [bios/] [keyboard.c] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 jamieiles
// Copyright Jamie Iles, 2017
2
//
3
// This file is part of s80x86.
4
//
5
// s80x86 is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// s80x86 is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with s80x86.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
#include "bda.h"
19
#include "bios.h"
20
#include "io.h"
21
#include "leds.h"
22
#include "scancodes.h"
23
#include "serial.h"
24
#include "utils.h"
25
 
26
#define PS2_DATA_PORT 0x60
27
#define PS2_CTRL_PORT 0x61
28
#define PS2_CTRL_ACK (1 << 0)
29
#define PS2_CTRL_RX_VALID (1 << 0)
30
 
31
static int kbd_buffer_full(void)
32
{
33
    unsigned short tail = bda_read(kbd_buffer_tail);
34
    unsigned short head = bda_read(kbd_buffer_head);
35
 
36
    // clang-format off
37
    return (tail == head - 2) ||
38
           (head == offsetof(struct bios_data_area, kbd_buffer) &&
39
            tail == (offsetof(struct bios_data_area, drive_recalibration_status) - 2));
40
    // clang-format on
41
}
42
 
43
void noinline kbd_buffer_add(unsigned short key)
44
{
45
    if (kbd_buffer_full()) {
46
        led_set(LED_KBD_BUFFER_FULL);
47
        return;
48
    }
49
 
50
    led_clear(LED_KBD_BUFFER_FULL);
51
    led_set(LED_KBD_BUFFER_NON_EMPTY);
52
 
53
    unsigned short tail = bda_read(kbd_buffer_tail);
54
    unsigned circ_offset = tail - offsetof(struct bios_data_area, kbd_buffer);
55
    bda_write(kbd_buffer[circ_offset / sizeof(unsigned short)], key);
56
 
57
    tail += sizeof(unsigned short);
58
    if (tail >= offsetof(struct bios_data_area, drive_recalibration_status))
59
        tail = offsetof(struct bios_data_area, kbd_buffer);
60
    bda_write(kbd_buffer_tail, tail);
61
}
62
 
63
static void kbd_buffer_pop(void)
64
{
65
    unsigned short head = bda_read(kbd_buffer_head);
66
 
67
    head += sizeof(unsigned short);
68
    if (head >= offsetof(struct bios_data_area, drive_recalibration_status))
69
        head = offsetof(struct bios_data_area, kbd_buffer);
70
    bda_write(kbd_buffer_head, head);
71
 
72
    if (head == bda_read(kbd_buffer_tail))
73
        led_clear(LED_KBD_BUFFER_NON_EMPTY);
74
}
75
 
76
unsigned kbd_buffer_peek(void)
77
{
78
    unsigned short head = bda_read(kbd_buffer_head);
79
    unsigned short tail = bda_read(kbd_buffer_tail);
80
 
81
    if (head == tail)
82
        return 0;
83
 
84
    unsigned circ_offset = head - offsetof(struct bios_data_area, kbd_buffer);
85
 
86
    return bda_read(kbd_buffer[circ_offset / sizeof(unsigned short)]);
87
}
88
 
89
#define SCANCODE_LSHIFT 0x12
90
#define SCANCODE_LCTRL 0x14
91
#define SCANCODE_LALT 0x11
92
 
93
#define KBD_FLAG_LSHIFT (1 << 1)
94
#define KBD_FLAG_LCTRL (1 << 2)
95
#define KBD_FLAG_LALT (1 << 3)
96
 
97
static int is_modifier(unsigned char b)
98
{
99
    return b == SCANCODE_LALT || b == SCANCODE_LCTRL || b == SCANCODE_LSHIFT;
100
}
101
 
102
void modifier_key(unsigned char b, int keyup)
103
{
104
    unsigned char flags = bda_read(keyboard_flags[0]);
105
    unsigned char mask = 0;
106
 
107
    if (b == SCANCODE_LALT)
108
        mask = KBD_FLAG_LALT;
109
    if (b == SCANCODE_LSHIFT)
110
        mask = KBD_FLAG_LSHIFT;
111
    if (b == SCANCODE_LCTRL)
112
        mask = KBD_FLAG_LCTRL;
113
 
114
    if (keyup)
115
        flags &= ~mask;
116
    else
117
        flags |= mask;
118
 
119
    bda_write(keyboard_flags[0], flags);
120
}
121
 
122
static int noinline get_kbd_flags(void)
123
{
124
    return bda_read(keyboard_flags[0]);
125
}
126
 
127
static void noinline keypress(const struct keydef *map, unsigned char b)
128
{
129
    const struct keydef *def = &map[b];
130
    unsigned short flags = get_kbd_flags();
131
 
132
    if ((flags & KBD_FLAG_LSHIFT) && def->shifted)
133
        kbd_buffer_add(def->shifted);
134
    else if ((flags & KBD_FLAG_LCTRL) && def->ctrl)
135
        kbd_buffer_add(def->ctrl);
136
    else if ((flags & KBD_FLAG_LALT) && def->alt)
137
        kbd_buffer_add(def->alt);
138
    else if (def->normal)
139
        kbd_buffer_add(def->normal);
140
}
141
 
142
static unsigned char get_scancode(void)
143
{
144
    unsigned char b;
145
 
146
    do {
147
        b = inb(PS2_DATA_PORT);
148
        if (b)
149
            outb(PS2_CTRL_PORT, PS2_CTRL_ACK);
150
    } while (!b);
151
 
152
    return b;
153
}
154
 
155
static int is_extended(unsigned char b)
156
{
157
    return b == 0xe0;
158
}
159
 
160
static void extended_key(void)
161
{
162
    keypress(extended_keycode_map, get_scancode());
163
}
164
 
165
static void keyboard_reset(void)
166
{
167
    // Reset
168
    outb(PS2_DATA_PORT, 0xff);
169
    // Enable
170
    outb(PS2_DATA_PORT, 0xf4);
171
    // Flush FIFO
172
    int i;
173
    for (i = 0; i < 64; ++i) {
174
        if (!(inb(PS2_CTRL_PORT) & PS2_CTRL_RX_VALID))
175
            break;
176
        outb(PS2_CTRL_PORT, PS2_CTRL_ACK);
177
    }
178
    if (i == 64)
179
        putstr("Warning: failed to empty Keyboard FIFO.\n");
180
}
181
 
182
static int keyboard_poll(void)
183
{
184
    unsigned char b = inb(PS2_DATA_PORT);
185
    if (!b)
186
        return 0;
187
 
188
    if (b == 0xaa) {
189
        keyboard_reset();
190
        return 1;
191
    }
192
 
193
    outb(PS2_CTRL_PORT, PS2_CTRL_ACK);
194
 
195
    int keyup = 0;
196
    if (b == 0xf0) {
197
        keyup = 1;
198
        b = get_scancode();
199
    }
200
 
201
    if (is_modifier(b))
202
        modifier_key(b, keyup);
203
    else if (is_extended(b))
204
        extended_key();
205
    else if (!keyup && b < ARRAY_SIZE(keycode_map))
206
        keypress(keycode_map, b);
207
 
208
    return 1;
209
}
210
 
211
static void kbd_irq(struct callregs *regs)
212
{
213
    irq_ack();
214
 
215
    while (keyboard_poll())
216
        continue;
217
}
218
VECTOR(0x9, kbd_irq);
219
 
220
static void keyboard_wait(struct callregs *regs)
221
{
222
    unsigned short c;
223
 
224
    regs->flags &= ~CF;
225
    do {
226
        c = kbd_buffer_peek();
227
        if (!c) {
228
#ifdef SERIAL_STDIO
229
            serial_poll();
230
#endif // SERIAL_STDIO
231
            keyboard_poll();
232
        }
233
    } while (c == 0);
234
 
235
    kbd_buffer_pop();
236
 
237
    regs->ax.x = c;
238
}
239
 
240
static void keyboard_status(struct callregs *regs)
241
{
242
    unsigned short c;
243
 
244
    regs->flags &= ~CF;
245
 
246
#ifdef SERIAL_STDIO
247
    c = kbd_buffer_peek();
248
    if (!c)
249
        serial_poll();
250
#endif
251
    c = kbd_buffer_peek();
252
    if (c)
253
        regs->flags &= ~ZF;
254
    else
255
        regs->flags |= ZF;
256
 
257
    regs->ax.x = c;
258
}
259
 
260
static void keyboard_shift_status(struct callregs *regs)
261
{
262
    regs->flags &= ~CF;
263
    regs->ax.h = 0;
264
    regs->ax.l = bda_read(keyboard_flags[0]);
265
}
266
 
267
static void keyboard_services(struct callregs *regs)
268
{
269
    regs->flags |= CF;
270
 
271
    switch (regs->ax.h) {
272
    case 0x0: // Fallthrough
273
    case 0x10: keyboard_wait(regs); break;
274
    case 0x1: // Fallthrough
275
    case 0x11: keyboard_status(regs); break;
276
    case 0x2: keyboard_shift_status(regs); break;
277
    default: break;
278
    }
279
}
280
VECTOR(0x16, keyboard_services);
281
 
282
void keyboard_init(void)
283
{
284
    bda_write(keyboard_flags[0], 0);
285
    bda_write(keyboard_flags[1], 0);
286
    bda_write(kbd_buffer_tail, offsetof(struct bios_data_area, kbd_buffer));
287
    bda_write(kbd_buffer_head, offsetof(struct bios_data_area, kbd_buffer));
288
 
289
    keyboard_reset();
290
 
291
    irq_enable(1);
292
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.