/[gxemul]/trunk/src/devices/dev_pckbc.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/devices/dev_pckbc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations)
Mon Oct 8 16:22:32 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 25350 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1613 2007/06/15 20:11:26 debug Exp $
20070501	Continuing a little on m88k disassembly (control registers,
		more instructions).
		Adding a dummy mvme88k machine mode.
20070502	Re-adding MIPS load/store alignment exceptions.
20070503	Implementing more of the M88K disassembly code.
20070504	Adding disassembly of some more M88K load/store instructions.
		Implementing some relatively simple M88K instructions (br.n,
		xor[.u] imm, and[.u] imm).
20070505	Implementing M88K three-register and, or, xor, and jmp[.n],
		bsr[.n] including function call trace stuff.
		Applying a patch from Bruce M. Simpson which implements the
		SYSCON_BOARD_CPU_CLOCK_FREQ_ID object of the syscon call in
		the yamon PROM emulation.
20070506	Implementing M88K bb0[.n] and bb1[.n], and skeletons for
		ldcr and stcr (although no control regs are implemented yet).
20070509	Found and fixed the bug which caused Linux for QEMU_MIPS to
		stop working in 0.4.5.1: It was a faulty change to the MIPS
		'sc' and 'scd' instructions I made while going through gcc -W
		warnings on 20070428.
20070510	Updating the Linux/QEMU_MIPS section in guestoses.html to
		use mips-test-0.2.tar.gz instead of 0.1.
		A big thank you to Miod Vallat for sending me M88K manuals.
		Implementing more M88K instructions (addu, subu, div[u], mulu,
		ext[u], clr, set, cmp).
20070511	Fixing bugs in the M88K "and" and "and.u" instructions (found
		by comparing against the manual).
		Implementing more M88K instructions (mask[.u], mak, bcnd (auto-
		generated)) and some more control register details.
		Cleanup: Removing the experimental AVR emulation mode and
		corresponding devices; AVR emulation wasn't really meaningful.
		Implementing autogeneration of most M88K loads/stores. The
		rectangle drawing demo (with -O0) for M88K runs :-)
		Beginning on M88K exception handling.
		More M88K instructions: tb0, tb1, rte, sub, jsr[.n].
		Adding some skeleton MVME PROM ("BUG") emulation.
20070512	Fixing a bug in the M88K cmp instruction.
		Adding the M88K lda (scaled register) instruction.
		Fixing bugs in 64-bit (32-bit pairs) M88K loads/stores.
		Removing the unused tick_hz stuff from the machine struct.
		Implementing the M88K xmem instruction. OpenBSD/mvme88k gets
		far enough to display the Copyright banner :-)
		Implementing subu.co (guess), addu.co, addu.ci, ff0, and ff1.
		Adding a dev_mvme187, for MVME187-specific devices/registers.
		OpenBSD/mvme88k prints more boot messages. :)
20070515	Continuing on MVME187 emulation (adding more devices, beginning
		on the CMMUs, etc).
		Adding the M88K and.c, xor.c, and or.c instructions, and making
		sure that mul, div, etc cause exceptions if executed when SFD1
		is disabled.
20070517	Continuing on M88K and MVME187 emulation in general; moving
		the CMMU registers to the CPU struct, separating dev_pcc2 from
		dev_mvme187, and beginning on memory_m88k.c (BATC and PATC).
		Fixing a bug in 64-bit (32-bit pairs) M88K fast stores.
		Implementing the clock part of dev_mk48txx.
		Implementing the M88K fstcr and xcr instructions.
		Implementing m88k_cpu_tlbdump().
		Beginning on the implementation of a separate address space
		for M88K .usr loads/stores.
20070520	Removing the non-working (skeleton) Sandpoint, SonyNEWS, SHARK
		Dnard, and Zaurus machine modes.
		Experimenting with dyntrans to_be_translated read-ahead. It
		seems to give a very small performance increase for MIPS
		emulation, but a large performance degradation for SuperH. Hm.
20070522	Disabling correct SuperH ITLB emulation; it does not seem to be
		necessary in order to let SH4 guest OSes run, and it slows down
		userspace code.
		Implementing "samepage" branches for SuperH emulation, and some
		other minor speed hacks.
20070525	Continuing on M88K memory-related stuff: exceptions, memory
		transaction register contents, etc.
		Implementing the M88K subu.ci instruction.
		Removing the non-working (skeleton) Iyonix machine mode.
		OpenBSD/mvme88k reaches userland :-), starts executing
		/sbin/init's instructions, and issues a few syscalls, before
		crashing.
20070526	Fixing bugs in dev_mk48txx, so that OpenBSD/mvme88k detects
		the correct time-of-day.
		Implementing a generic IRQ controller for the test machines
		(dev_irqc), similar to a proposed patch from Petr Stepan.
		Experimenting some more with translation read-ahead.
		Adding an "expect" script for automated OpenBSD/landisk
		install regression/performance tests.
20070527	Adding a dummy mmEye (SH3) machine mode skeleton.
		FINALLY found the strange M88K bug I have been hunting: I had
		not emulated the SNIP value for exceptions occurring in
		branch delay slots correctly.
		Implementing correct exceptions for 64-bit M88K loads/stores.
		Address to symbol lookups are now disabled when M88K is
		running in usermode (because usermode addresses don't have
		anything to do with supervisor addresses).
20070531	Removing the mmEye machine mode skeleton.
20070604	Some minor code cleanup.
20070605	Moving src/useremul.c into a subdir (src/useremul/), and
		cleaning up some more legacy constructs.
		Adding -Wstrict-aliasing and -fstrict-aliasing detection to
		the configure script.
20070606	Adding a check for broken GCC on Solaris to the configure
		script. (GCC 3.4.3 on Solaris cannot handle static variables
		which are initialized to 0 or NULL. :-/)
		Removing the old (non-working) ARC emulation modes: NEC RD94,
		R94, R96, and R98, and the last traces of Olivetti M700 and
		Deskstation Tyne.
		Removing the non-working skeleton WDSC device (dev_wdsc).
20070607	Thinking about how to use the host's cc + ld at runtime to
		generate native code. (See experiments/native_cc_ld_test.i
		for an example.)
20070608	Adding a program counter sampling timer, which could be useful
		for native code generation experiments.
		The KN02_CSR_NRMMOD bit in the DECstation 5000/200 (KN02) CSR
		should always be set, to allow a 5000/200 PROM to boot.
20070609	Moving out breakpoint details from the machine struct into
		a helper struct, and removing the limit on max nr of
		breakpoints.
20070610	Moving out tick functions into a helper struct as well (which
		also gets rid of the max limit).
20070612	FINALLY figured out why Debian/DECstation stopped working when
		translation read-ahead was enabled: in src/memory_rw.c, the
		call to invalidate_code_translation was made also if the
		memory access was an instruction load (if the page was mapped
		as writable); it shouldn't be called in that case.
20070613	Implementing some more MIPS32/64 revision 2 instructions: di,
		ei, ext, dext, dextm, dextu, and ins.
20070614	Implementing an instruction combination for the NetBSD/arm
		idle loop (making the host not use any cpu if NetBSD/arm
		inside the emulator is not using any cpu).
		Increasing the nr of ARM VPH entries from 128 to 384.
20070615	Removing the ENABLE_arch stuff from the configure script, so
		that all included architectures are included in both release
		and development builds.
		Moving memory related helper functions from misc.c to memory.c.
		Adding preliminary instructions for netbooting NetBSD/pmppc to
		guestoses.html; it doesn't work yet, there are weird timeouts.
		Beginning a total rewrite of the userland emulation modes
		(removing all emulation modes, beginning from scratch with
		NetBSD/MIPS and FreeBSD/Alpha only).
20070616	After fixing a bug in the DEC21143 NIC (the TDSTAT_OWN bit was
		only cleared for the last segment when transmitting, not all
		segments), NetBSD/pmppc boots with root-on-nfs without the
		timeouts. Updating guestoses.html.
		Removing the skeleton PSP (Playstation Portable) mode.
		Moving X11-related stuff in the machine struct into a helper
		struct.
		Cleanup of out-of-memory checks, to use a new CHECK_ALLOCATION
		macro (which prints a meaningful error message).
		Adding a COMMENT to each machine and device (for automagic
		.index comment generation).
		Doing regression testing for the next release.

==============  RELEASE 0.4.6  ==============


1 /*
2 * Copyright (C) 2003-2007 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_pckbc.c,v 1.74 2007/06/15 19:57:33 debug Exp $
29 *
30 * COMMENT: 8042 PC keyboard controller (+ 8242WB Keyboard/Mouse controller)
31 *
32 * This module includes emulation of the 8048 keyboard chip too.
33 *
34 * Quick source of good info: http://my.execpc.com/~geezer/osd/kbd/kbd.txt
35 *
36 *
37 * TODOs:
38 * Finish the rewrite for 8242.
39 */
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include "console.h"
46 #include "cpu.h"
47 #include "devices.h"
48 #include "machine.h"
49 #include "memory.h"
50 #include "misc.h"
51
52 #include "kbdreg.h"
53
54
55 /* #define PCKBC_DEBUG */
56 /* #define debug fatal */
57
58
59 #define MAX_8042_QUEUELEN 256
60
61 #define PC_DATA 0
62 #define PC_CMD 0
63 #define PC_STATUS 1
64
65 #define PS2_TXBUF 0
66 #define PS2_RXBUF 1
67 #define PS2_CONTROL 2
68 #define PS2_STATUS 3
69
70 #define PS2 100
71
72 #define PCKBC_TICKSHIFT 15
73
74 struct pckbc_data {
75 int console_handle;
76 int in_use;
77
78 int reg[DEV_PCKBC_LENGTH];
79
80 struct interrupt irq_keyboard;
81 struct interrupt irq_mouse;
82 int currently_asserted[2];
83 int type;
84 int pc_style_flag;
85
86 /* TODO: one of these for each port? */
87 int clocksignal;
88 int rx_int_enable;
89 int tx_int_enable;
90
91 int keyscanning_enabled;
92 int translation_table;
93 int state;
94 int cmdbyte;
95 int output_byte;
96 int last_scancode;
97
98 unsigned key_queue[2][MAX_8042_QUEUELEN];
99 int head[2], tail[2];
100 };
101
102 #define STATE_NORMAL 0
103 #define STATE_LDCMDBYTE 1
104 #define STATE_RDCMDBYTE 2
105 #define STATE_WAITING_FOR_TRANSLTABLE 3
106 #define STATE_WAITING_FOR_RATE 4
107 #define STATE_WAITING_FOR_ONEKEY_MB 5
108 #define STATE_WAITING_FOR_AUX 6
109 #define STATE_WAITING_FOR_AUX_OUT 7
110 #define STATE_LDOUTPUT 8
111 #define STATE_RDOUTPUT 9
112
113
114 /*
115 * pckbc_add_code():
116 *
117 * Adds a byte to the data queue.
118 */
119 void pckbc_add_code(struct pckbc_data *d, int code, int port)
120 {
121 /* Add at the head, read at the tail: */
122 d->head[port] = (d->head[port]+1) % MAX_8042_QUEUELEN;
123 if (d->head[port] == d->tail[port])
124 fatal("[ pckbc: queue overrun, port %i! ]\n", port);
125
126 d->key_queue[port][d->head[port]] = code;
127 }
128
129
130 /*
131 * pckbc_get_code():
132 *
133 * Reads a byte from a data queue.
134 */
135 int pckbc_get_code(struct pckbc_data *d, int port)
136 {
137 if (d->head[port] == d->tail[port])
138 fatal("[ pckbc: queue empty, port %i! ]\n", port);
139 else
140 d->tail[port] = (d->tail[port]+1) % MAX_8042_QUEUELEN;
141 return d->key_queue[port][d->tail[port]];
142 }
143
144
145 /*
146 * ascii_to_scancodes_type3():
147 *
148 * Conversion from ASCII codes to default (US) keyboard scancodes.
149 * (See http://www.computer-engineering.org/ps2keyboard/scancodes3.html)
150 */
151 static void ascii_to_pc_scancodes_type3(int a, struct pckbc_data *d)
152 {
153 int old_head;
154 int p = 0; /* port */
155 int shift = 0, ctrl = 0;
156
157 if (a >= 'A' && a <= 'Z') { a += 32; shift = 1; }
158 if ((a >= 1 && a <= 26) && (a!='\n' && a!='\t' && a!='\b' && a!='\r'))
159 { a += 96; ctrl = 1; }
160 if (a=='!') { a = '1'; shift = 1; }
161 if (a=='@') { a = '2'; shift = 1; }
162 if (a=='#') { a = '3'; shift = 1; }
163 if (a=='$') { a = '4'; shift = 1; }
164 if (a=='%') { a = '5'; shift = 1; }
165 if (a=='^') { a = '6'; shift = 1; }
166 if (a=='&') { a = '7'; shift = 1; }
167 if (a=='*') { a = '8'; shift = 1; }
168 if (a=='(') { a = '9'; shift = 1; }
169 if (a==')') { a = '0'; shift = 1; }
170 if (a=='_') { a = '-'; shift = 1; }
171 if (a=='+') { a = '='; shift = 1; }
172 if (a=='{') { a = '['; shift = 1; }
173 if (a=='}') { a = ']'; shift = 1; }
174 if (a==':') { a = ';'; shift = 1; }
175 if (a=='"') { a = '\''; shift = 1; }
176 if (a=='|') { a = '\\'; shift = 1; }
177 if (a=='<') { a = ','; shift = 1; }
178 if (a=='>') { a = '.'; shift = 1; }
179 if (a=='?') { a = '/'; shift = 1; }
180
181 if (shift)
182 pckbc_add_code(d, 0x12, p);
183 if (ctrl)
184 pckbc_add_code(d, 0x11, p);
185
186 /*
187 * Note: The ugly hack used to add release codes for all of these
188 * keys is as follows: we remember how much of the kbd buf that
189 * is in use here, before we add any scancode. After we've added
190 * one or more scancodes (ie an optional shift + another key)
191 * then we add 0xf0 + the last scancode _if_ the kbd buf was altered.
192 */
193
194 old_head = d->head[p];
195
196 if (a==27) pckbc_add_code(d, 0x08, p);
197
198 if (a=='1') pckbc_add_code(d, 0x16, p);
199 if (a=='2') pckbc_add_code(d, 0x1e, p);
200 if (a=='3') pckbc_add_code(d, 0x26, p);
201 if (a=='4') pckbc_add_code(d, 0x25, p);
202 if (a=='5') pckbc_add_code(d, 0x2e, p);
203 if (a=='6') pckbc_add_code(d, 0x36, p);
204 if (a=='7') pckbc_add_code(d, 0x3d, p);
205 if (a=='8') pckbc_add_code(d, 0x3e, p);
206 if (a=='9') pckbc_add_code(d, 0x46, p);
207 if (a=='0') pckbc_add_code(d, 0x45, p);
208 if (a=='-') pckbc_add_code(d, 0x4e, p);
209 if (a=='=') pckbc_add_code(d, 0x55, p);
210
211 if (a=='\b') pckbc_add_code(d, 0x29, p);
212
213 if (a=='\t') pckbc_add_code(d, 0x0d, p);
214 if (a=='q') pckbc_add_code(d, 0x15, p);
215 if (a=='w') pckbc_add_code(d, 0x1d, p);
216 if (a=='e') pckbc_add_code(d, 0x24, p);
217 if (a=='r') pckbc_add_code(d, 0x2d, p);
218 if (a=='t') pckbc_add_code(d, 0x2c, p);
219 if (a=='y') pckbc_add_code(d, 0x35, p);
220 if (a=='u') pckbc_add_code(d, 0x3c, p);
221 if (a=='i') pckbc_add_code(d, 0x43, p);
222 if (a=='o') pckbc_add_code(d, 0x44, p);
223 if (a=='p') pckbc_add_code(d, 0x4d, p);
224
225 if (a=='[') pckbc_add_code(d, 0x54, p);
226 if (a==']') pckbc_add_code(d, 0x5b, p);
227
228 if (a=='\n' || a=='\r') pckbc_add_code(d, 0x5a, p);
229
230 if (a=='a') pckbc_add_code(d, 0x1c, p);
231 if (a=='s') pckbc_add_code(d, 0x1b, p);
232 if (a=='d') pckbc_add_code(d, 0x23, p);
233 if (a=='f') pckbc_add_code(d, 0x2b, p);
234 if (a=='g') pckbc_add_code(d, 0x34, p);
235 if (a=='h') pckbc_add_code(d, 0x33, p);
236 if (a=='j') pckbc_add_code(d, 0x3b, p);
237 if (a=='k') pckbc_add_code(d, 0x42, p);
238 if (a=='l') pckbc_add_code(d, 0x4b, p);
239
240 if (a==';') pckbc_add_code(d, 0x4c, p);
241 if (a=='\'') pckbc_add_code(d, 0x52, p);
242 /* if (a=='~') pckbc_add_code(d, 0x29, p); ? */
243 if (a=='\\') pckbc_add_code(d, 0x5c, p);
244
245 if (a=='z') pckbc_add_code(d, 0x1a, p);
246 if (a=='x') pckbc_add_code(d, 0x22, p);
247 if (a=='c') pckbc_add_code(d, 0x21, p);
248 if (a=='v') pckbc_add_code(d, 0x2a, p);
249 if (a=='b') pckbc_add_code(d, 0x32, p);
250 if (a=='n') pckbc_add_code(d, 0x31, p);
251 if (a=='m') pckbc_add_code(d, 0x3a, p);
252
253 if (a==',') pckbc_add_code(d, 0x41, p);
254 if (a=='.') pckbc_add_code(d, 0x49, p);
255 if (a=='/') pckbc_add_code(d, 0x4a, p);
256
257 if (a==' ') pckbc_add_code(d, 0x29, p);
258
259 /* Add release code, if a key was pressed: */
260 if (d->head[p] != old_head) {
261 int code = d->key_queue[p][d->head[p]];
262 pckbc_add_code(d, 0xf0, p);
263 pckbc_add_code(d, code, p);
264 }
265
266 /* Release shift and ctrl: */
267 if (shift) {
268 pckbc_add_code(d, 0xf0, p);
269 pckbc_add_code(d, 0x12, p);
270 }
271 if (ctrl) {
272 pckbc_add_code(d, 0xf0, p);
273 pckbc_add_code(d, 0x11, p);
274 }
275 }
276
277
278 /*
279 * ascii_to_scancodes_type2():
280 *
281 * Conversion from ASCII codes to default (US) keyboard scancodes.
282 * (See http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html)
283 *
284 * NOTE/TODO: This seems to be type 2, not type 1.
285 */
286 static void ascii_to_pc_scancodes_type2(int a, struct pckbc_data *d)
287 {
288 int old_head;
289 int p = 0; /* port */
290 int shift = 0, ctrl = 0;
291
292 if (d->translation_table == 3) {
293 ascii_to_pc_scancodes_type3(a, d);
294 return;
295 }
296
297 if (d->translation_table != 2) {
298 fatal("[ ascii_to_pc_scancodes: unimplemented type! ]\n");
299 return;
300 }
301
302 if (a >= 'A' && a <= 'Z') { a += 32; shift = 1; }
303 if ((a >= 1 && a <= 26) && (a!='\n' && a!='\t' && a!='\b' && a!='\r'))
304 { a += 96; ctrl = 1; }
305
306 if (a=='!') { a = '1'; shift = 1; }
307 if (a=='@') { a = '2'; shift = 1; }
308 if (a=='#') { a = '3'; shift = 1; }
309 if (a=='$') { a = '4'; shift = 1; }
310 if (a=='%') { a = '5'; shift = 1; }
311 if (a=='^') { a = '6'; shift = 1; }
312 if (a=='&') { a = '7'; shift = 1; }
313 if (a=='*') { a = '8'; shift = 1; }
314 if (a=='(') { a = '9'; shift = 1; }
315 if (a==')') { a = '0'; shift = 1; }
316 if (a=='_') { a = '-'; shift = 1; }
317 if (a=='+') { a = '='; shift = 1; }
318 if (a=='{') { a = '['; shift = 1; }
319 if (a=='}') { a = ']'; shift = 1; }
320 if (a==':') { a = ';'; shift = 1; }
321 if (a=='"') { a = '\''; shift = 1; }
322 if (a=='|') { a = '\\'; shift = 1; }
323 if (a=='<') { a = ','; shift = 1; }
324 if (a=='>') { a = '.'; shift = 1; }
325 if (a=='?') { a = '/'; shift = 1; }
326 if (a=='~') { a = '`'; shift = 1; }
327
328 if (shift)
329 pckbc_add_code(d, 0x2a, p);
330 else
331 pckbc_add_code(d, 0x2a + 0x80, p);
332
333 if (ctrl)
334 pckbc_add_code(d, 0x1d, p);
335
336 /*
337 * Note: The ugly hack used to add release codes for all of these
338 * keys is as follows: we remember how much of the kbd buf that
339 * is in use here, before we add any scancode. After we've added
340 * one or more scancodes (ie an optional shift + another key)
341 * then we duplicate the last scancode | 0x80 _if_ the kbd buf
342 * was altered.
343 */
344
345 old_head = d->head[p];
346
347 if (a==27) pckbc_add_code(d, 0x01, p);
348
349 if (a=='1') pckbc_add_code(d, 0x02, p);
350 if (a=='2') pckbc_add_code(d, 0x03, p);
351 if (a=='3') pckbc_add_code(d, 0x04, p);
352 if (a=='4') pckbc_add_code(d, 0x05, p);
353 if (a=='5') pckbc_add_code(d, 0x06, p);
354 if (a=='6') pckbc_add_code(d, 0x07, p);
355 if (a=='7') pckbc_add_code(d, 0x08, p);
356 if (a=='8') pckbc_add_code(d, 0x09, p);
357 if (a=='9') pckbc_add_code(d, 0x0a, p);
358 if (a=='0') pckbc_add_code(d, 0x0b, p);
359 if (a=='-') pckbc_add_code(d, 0x0c, p);
360 if (a=='=') pckbc_add_code(d, 0x0d, p);
361
362 if (a=='!') { pckbc_add_code(d, 0x2a, p);
363 pckbc_add_code(d, 0x02, p); }
364 if (a=='@') { pckbc_add_code(d, 0x2a, p);
365 pckbc_add_code(d, 0x03, p); }
366 if (a=='#') { pckbc_add_code(d, 0x2a, p);
367 pckbc_add_code(d, 0x04, p); }
368 if (a=='$') { pckbc_add_code(d, 0x2a, p);
369 pckbc_add_code(d, 0x05, p); }
370 if (a=='%') { pckbc_add_code(d, 0x2a, p);
371 pckbc_add_code(d, 0x06, p); }
372 if (a=='^') { pckbc_add_code(d, 0x2a, p);
373 pckbc_add_code(d, 0x07, p); }
374 if (a=='&') { pckbc_add_code(d, 0x2a, p);
375 pckbc_add_code(d, 0x08, p); }
376 if (a=='*') { pckbc_add_code(d, 0x2a, p);
377 pckbc_add_code(d, 0x09, p); }
378 if (a=='(') { pckbc_add_code(d, 0x2a, p);
379 pckbc_add_code(d, 0x0a, p); }
380
381 if (a=='\b') pckbc_add_code(d, 0x0e, p);
382
383 if (a=='\t') pckbc_add_code(d, 0x0f, p);
384 if (a=='q') pckbc_add_code(d, 0x10, p);
385 if (a=='w') pckbc_add_code(d, 0x11, p);
386 if (a=='e') pckbc_add_code(d, 0x12, p);
387 if (a=='r') pckbc_add_code(d, 0x13, p);
388 if (a=='t') pckbc_add_code(d, 0x14, p);
389 if (a=='y') pckbc_add_code(d, 0x15, p);
390 if (a=='u') pckbc_add_code(d, 0x16, p);
391 if (a=='i') pckbc_add_code(d, 0x17, p);
392 if (a=='o') pckbc_add_code(d, 0x18, p);
393 if (a=='p') pckbc_add_code(d, 0x19, p);
394
395 if (a=='[') pckbc_add_code(d, 0x1a, p);
396 if (a==']') pckbc_add_code(d, 0x1b, p);
397
398 if (a=='\n' || a=='\r') pckbc_add_code(d, 0x1c, p);
399
400 if (a=='a') pckbc_add_code(d, 0x1e, p);
401 if (a=='s') pckbc_add_code(d, 0x1f, p);
402 if (a=='d') pckbc_add_code(d, 0x20, p);
403 if (a=='f') pckbc_add_code(d, 0x21, p);
404 if (a=='g') pckbc_add_code(d, 0x22, p);
405 if (a=='h') pckbc_add_code(d, 0x23, p);
406 if (a=='j') pckbc_add_code(d, 0x24, p);
407 if (a=='k') pckbc_add_code(d, 0x25, p);
408 if (a=='l') pckbc_add_code(d, 0x26, p);
409
410 if (a==';') pckbc_add_code(d, 0x27, p);
411 if (a=='\'') pckbc_add_code(d, 0x28, p);
412 if (a=='`') pckbc_add_code(d, 0x29, p);
413 if (a=='\\') pckbc_add_code(d, 0x2b, p);
414
415 if (a=='z') pckbc_add_code(d, 0x2c, p);
416 if (a=='x') pckbc_add_code(d, 0x2d, p);
417 if (a=='c') pckbc_add_code(d, 0x2e, p);
418 if (a=='v') pckbc_add_code(d, 0x2f, p);
419 if (a=='b') pckbc_add_code(d, 0x30, p);
420 if (a=='n') pckbc_add_code(d, 0x31, p);
421 if (a=='m') pckbc_add_code(d, 0x32, p);
422
423 if (a==',') pckbc_add_code(d, 0x33, p);
424 if (a=='.') pckbc_add_code(d, 0x34, p);
425 if (a=='/') pckbc_add_code(d, 0x35, p);
426
427 if (a==' ') pckbc_add_code(d, 0x39, p);
428
429 /* Add release code, if a key was pressed: */
430 if (d->head[p] != old_head) {
431 int code = d->key_queue[p][d->head[p]] | 0x80;
432 pckbc_add_code(d, code, p);
433 }
434
435 /* Release ctrl: */
436 if (ctrl)
437 pckbc_add_code(d, 0x1d + 0x80, p);
438 }
439
440
441 DEVICE_TICK(pckbc)
442 {
443 struct pckbc_data *d = extra;
444 int port_nr, ch, ints_enabled;
445
446 if (d->in_use && console_charavail(d->console_handle)) {
447 ch = console_readchar(d->console_handle);
448 if (ch >= 0)
449 ascii_to_pc_scancodes_type2(ch, d);
450 }
451
452 ints_enabled = d->rx_int_enable;
453
454 /* TODO: mouse movements? */
455
456 if (d->cmdbyte & KC8_KDISABLE)
457 ints_enabled = 0;
458
459 for (port_nr=0; port_nr<2; port_nr++) {
460 /* Cause receive interrupt, if there's something in the
461 receive buffer: (Otherwise deassert the interrupt.) */
462
463 if (d->head[port_nr] != d->tail[port_nr] && ints_enabled) {
464 debug("[ pckbc: interrupt port %i ]\n", port_nr);
465 if (port_nr == 0)
466 INTERRUPT_ASSERT(d->irq_keyboard);
467 else
468 INTERRUPT_ASSERT(d->irq_mouse);
469 d->currently_asserted[port_nr] = 1;
470 } else {
471 if (d->currently_asserted[port_nr]) {
472 if (port_nr == 0)
473 INTERRUPT_DEASSERT(d->irq_keyboard);
474 else
475 INTERRUPT_DEASSERT(d->irq_mouse);
476 }
477 d->currently_asserted[port_nr] = 0;
478 }
479 }
480 }
481
482
483 /*
484 * dev_pckbc_command():
485 *
486 * Handle commands to the 8048 in the emulated keyboard.
487 */
488 static void dev_pckbc_command(struct pckbc_data *d, int port_nr)
489 {
490 int cmd = d->reg[PC_CMD];
491
492 if (d->type == PCKBC_8242)
493 cmd = d->reg[PS2_TXBUF];
494
495 if (d->state == STATE_WAITING_FOR_TRANSLTABLE) {
496 debug("[ pckbc: (port %i) switching to translation table "
497 "0x%02x ]\n", port_nr, cmd);
498 switch (cmd) {
499 case 2:
500 case 3: d->translation_table = cmd;
501 break;
502 default:fatal("[ pckbc: (port %i) translation table "
503 "0x%02x is NOT YET IMPLEMENTED ]\n",
504 port_nr, cmd);
505 }
506 pckbc_add_code(d, KBR_ACK, port_nr);
507 d->state = STATE_NORMAL;
508 return;
509 }
510
511 if (d->state == STATE_WAITING_FOR_RATE) {
512 debug("[ pckbc: (port %i) received Typematic Rate data: "
513 "0x%02x ]\n", port_nr, cmd);
514 pckbc_add_code(d, KBR_ACK, port_nr);
515 d->state = STATE_NORMAL;
516 return;
517 }
518
519 if (d->state == STATE_WAITING_FOR_ONEKEY_MB) {
520 debug("[ pckbc: (port %i) received One-key make/break data: "
521 "0x%02x ]\n", port_nr, cmd);
522 pckbc_add_code(d, KBR_ACK, port_nr);
523 d->state = STATE_NORMAL;
524 return;
525 }
526
527 if (d->state == STATE_WAITING_FOR_AUX) {
528 debug("[ pckbc: (port %i) received aux data: "
529 "0x%02x ]\n", port_nr, cmd);
530 /* Echo back. */
531 pckbc_add_code(d, cmd, port_nr);
532 d->state = STATE_NORMAL;
533 return;
534 }
535
536 if (d->state == STATE_WAITING_FOR_AUX_OUT) {
537 debug("[ pckbc: (port %i) received aux out data: "
538 "0x%02x ]\n", port_nr, cmd);
539 /* Echo back. */
540 pckbc_add_code(d, cmd, port_nr);
541 d->state = STATE_NORMAL;
542 return;
543 }
544
545 switch (cmd) {
546
547 case 0x00:
548 /*
549 * TODO: What does this do? This is possibly due to an
550 * error in the handling of some other command code.
551 */
552 pckbc_add_code(d, KBR_ACK, port_nr);
553 break;
554
555 case KBC_MODEIND: /* Set LEDs */
556 /* Just ACK, no LEDs are actually set. */
557 pckbc_add_code(d, KBR_ACK, port_nr);
558 break;
559
560 case KBC_SETTABLE:
561 pckbc_add_code(d, KBR_ACK, port_nr);
562 d->state = STATE_WAITING_FOR_TRANSLTABLE;
563 break;
564
565 case KBC_ENABLE:
566 d->keyscanning_enabled = 1;
567 pckbc_add_code(d, KBR_ACK, port_nr);
568 break;
569
570 case KBC_DISABLE:
571 d->keyscanning_enabled = 0;
572 pckbc_add_code(d, KBR_ACK, port_nr);
573 break;
574
575 case KBC_SETDEFAULT:
576 pckbc_add_code(d, KBR_ACK, port_nr);
577 break;
578
579 case KBC_GETID:
580 /* Get keyboard ID. NOTE/TODO: Ugly hardcoded answer. */
581 pckbc_add_code(d, KBR_ACK, port_nr);
582 pckbc_add_code(d, 0xab, port_nr);
583 pckbc_add_code(d, 0x41, port_nr);
584 break;
585
586 case KBC_TYPEMATIC:
587 /* Set typematic (auto-repeat) delay/speed: */
588 pckbc_add_code(d, KBR_ACK, port_nr);
589 d->state = STATE_WAITING_FOR_RATE;
590 break;
591
592 case KBC_ALLKEYS_TMB:
593 /* "Make all keys typematic/make/break" */
594 pckbc_add_code(d, KBR_ACK, port_nr);
595 break;
596
597 case KBC_ONEKEY_MB:
598 /* "Make one key typematic/make/break" */
599 pckbc_add_code(d, KBR_ACK, port_nr);
600 d->state = STATE_WAITING_FOR_ONEKEY_MB;
601 break;
602
603 case KBC_RESET:
604 pckbc_add_code(d, KBR_ACK, port_nr);
605 pckbc_add_code(d, KBR_RSTDONE, port_nr);
606 /*
607 * Disable interrupts during reset, or Linux 2.6
608 * prints warnings about spurious interrupts.
609 */
610 d->rx_int_enable = 0;
611 break;
612
613 default:
614 fatal("[ pckbc: (port %i) UNIMPLEMENTED 8048 command"
615 " 0x%02x ]\n", port_nr, cmd);
616 exit(1);
617 }
618 }
619
620
621 DEVICE_ACCESS(pckbc)
622 {
623 struct pckbc_data *d = extra;
624 uint64_t idata = 0, odata = 0;
625 int port_nr = 0;
626 size_t i;
627
628 if (writeflag == MEM_WRITE)
629 idata = memory_readmax64(cpu, data, len);
630
631 #ifdef PCKBC_DEBUG
632 if (writeflag == MEM_WRITE)
633 fatal("[ pckbc: write to addr 0x%x: 0x%x ]\n",
634 (int)relative_addr, (int)idata);
635 else
636 fatal("[ pckbc: read from addr 0x%x ]\n",
637 (int)relative_addr);
638 #endif
639
640 /* For JAZZ-based machines: */
641 if (relative_addr >= 0x60) {
642 relative_addr -= 0x60;
643 if (relative_addr != 0)
644 relative_addr = 1;
645 } else if (d->type == PCKBC_8242) {
646 /* 8242 PS2-style: */
647 /* when using 8-byte alignment... */
648 relative_addr /= sizeof(uint64_t);
649 /* port_nr = 0 for keyboard, 1 for mouse */
650 port_nr = (relative_addr >> 2);
651 relative_addr &= 3;
652 relative_addr += PS2;
653 } else if (d->pc_style_flag) {
654 /* PC-style: */
655 if (relative_addr != 0 && relative_addr != 4) {
656 /* TODO (port 0x61) */
657 odata = 0x21;
658 {
659 static int x = 0;
660 x++;
661 if (x&1)
662 odata ^= 0x10;
663 }
664 if (writeflag == MEM_READ)
665 memory_writemax64(cpu, data, len, odata);
666 return 1;
667 }
668 if (relative_addr != 0)
669 relative_addr = 1;
670 } else {
671 /* Others... Non-Jazz ARC-based machines etc. */
672 if (relative_addr != 0)
673 relative_addr = 1;
674 }
675
676 switch (relative_addr) {
677
678 /*
679 * 8042 (PC):
680 */
681
682 case 0: /* data */
683 if (writeflag==MEM_READ) {
684 switch (d->state) {
685 case STATE_RDCMDBYTE:
686 odata = d->cmdbyte;
687 d->state = STATE_NORMAL;
688 break;
689 case STATE_RDOUTPUT:
690 odata = d->output_byte;
691 d->state = STATE_NORMAL;
692 break;
693 default:if (d->head[0] != d->tail[0]) {
694 odata = pckbc_get_code(d, 0);
695 d->last_scancode = odata;
696 } else {
697 odata = d->last_scancode;
698 d->last_scancode |= 0x80;
699 }
700 }
701 /* debug("[ pckbc: read from DATA: 0x%02x ]\n",
702 (int)odata); */
703 } else {
704 debug("[ pckbc: write to DATA:");
705 for (i=0; i<len; i++)
706 debug(" %02x", data[i]);
707 debug(" ]\n");
708
709 switch (d->state) {
710 case STATE_LDCMDBYTE:
711 d->cmdbyte = idata;
712 d->rx_int_enable = d->cmdbyte &
713 (KC8_KENABLE | KC8_MENABLE) ? 1 : 0;
714 d->state = STATE_NORMAL;
715 break;
716 case STATE_LDOUTPUT:
717 d->output_byte = idata;
718 d->state = STATE_NORMAL;
719 break;
720 default:d->reg[relative_addr] = idata;
721 dev_pckbc_command(d, port_nr);
722 }
723 }
724 break;
725 case 1: /* control */
726 if (writeflag==MEM_READ) {
727 dev_pckbc_tick(cpu, d);
728
729 odata = 0;
730
731 /* "Data in buffer" bit */
732 if (d->head[0] != d->tail[0] ||
733 d->state == STATE_RDCMDBYTE ||
734 d->state == STATE_RDOUTPUT)
735 odata |= KBS_DIB;
736
737 if (d->state == STATE_RDCMDBYTE)
738 odata |= KBS_OCMD;
739
740 odata |= KBS_NOSEC;
741 /* debug("[ pckbc: read from CTL status port: "
742 "0x%02x ]\n", (int)odata); */
743 } else {
744 debug("[ pckbc: write to CTL:");
745 for (i=0; i<len; i++)
746 debug(" %02x", data[i]);
747 debug(" ]\n");
748 d->reg[relative_addr] = idata;
749
750 switch (idata) {
751 case 0x10:
752 case 0x11:
753 /* TODO: For now, don't print warnings about
754 these. NetBSD sends these. */
755 break;
756 case K_RDCMDBYTE:
757 d->state = STATE_RDCMDBYTE;
758 break;
759 case K_LDCMDBYTE:
760 d->state = STATE_LDCMDBYTE;
761 break;
762 case 0xa7:
763 d->cmdbyte |= KC8_MDISABLE;
764 break;
765 case 0xa8:
766 d->cmdbyte &= ~KC8_MDISABLE;
767 break;
768 case 0xa9: /* test auxiliary port */
769 debug("[ pckbc: CONTROL 0xa9, TODO ]\n");
770 break;
771 case 0xaa: /* keyboard self-test */
772 pckbc_add_code(d, 0x55, port_nr);
773 break;
774 case 0xab: /* keyboard interface self-test */
775 pckbc_add_code(d, 0x00, port_nr);
776 break;
777 case 0xad:
778 d->cmdbyte |= KC8_KDISABLE;
779 break;
780 case 0xae:
781 d->cmdbyte &= ~KC8_KDISABLE;
782 break;
783 case 0xd0:
784 d->state = STATE_RDOUTPUT;
785 break;
786 case 0xd1:
787 d->state = STATE_LDOUTPUT;
788 break;
789 case 0xd3: /* write to auxiliary device
790 output buffer */
791 debug("[ pckbc: CONTROL 0xd3, TODO ]\n");
792 d->state = STATE_WAITING_FOR_AUX_OUT;
793 break;
794 case 0xd4: /* write to auxiliary port */
795 debug("[ pckbc: CONTROL 0xd4, TODO ]\n");
796 d->state = STATE_WAITING_FOR_AUX;
797 break;
798 default:
799 fatal("[ pckbc: unknown CONTROL 0x%x ]\n",
800 (int)idata);
801 d->state = STATE_NORMAL;
802 }
803 }
804 break;
805
806 /*
807 * 8242 (PS2):
808 */
809
810 case PS2 + PS2_TXBUF:
811 if (writeflag==MEM_READ) {
812 odata = random() & 0xff;
813 debug("[ pckbc: read from port %i, PS2_TXBUF: "
814 "0x%x ]\n", port_nr, (int)odata);
815 } else {
816 debug("[ pckbc: write to port %i, PS2_TXBUF: "
817 "0x%llx ]\n", port_nr, (long long)idata);
818
819 /* Handle keyboard commands: */
820 d->reg[PS2_TXBUF] = idata;
821 dev_pckbc_command(d, port_nr);
822 }
823 break;
824
825 case PS2 + PS2_RXBUF:
826 if (writeflag==MEM_READ) {
827 /* TODO: What should be returned if no data
828 is available? */
829 odata = random() & 0xff;
830 if (d->head[port_nr] != d->tail[port_nr])
831 odata = pckbc_get_code(d, port_nr);
832 debug("[ pckbc: read from port %i, PS2_RXBUF: "
833 "0x%02x ]\n", port_nr, (int)odata);
834 } else {
835 debug("[ pckbc: write to port %i, PS2_RXBUF: "
836 "0x%llx ]\n", port_nr, (long long)idata);
837 }
838 break;
839
840 case PS2 + PS2_CONTROL:
841 if (writeflag==MEM_READ) {
842 debug("[ pckbc: read from port %i, PS2_CONTROL"
843 " ]\n", port_nr);
844 } else {
845 debug("[ pckbc: write to port %i, PS2_CONTROL:"
846 " 0x%llx ]\n", port_nr, (long long)idata);
847 d->clocksignal = (idata & 0x10) ? 1 : 0;
848 d->rx_int_enable = (idata & 0x08) ? 1 : 0;
849 d->tx_int_enable = (idata & 0x04) ? 1 : 0;
850 }
851 break;
852
853 case PS2 + PS2_STATUS:
854 if (writeflag==MEM_READ) {
855 /* 0x08 = transmit buffer empty */
856 odata = d->clocksignal + 0x08;
857
858 if (d->head[port_nr] != d->tail[port_nr]) {
859 /* 0x10 = receicer data available (?) */
860 odata |= 0x10;
861 }
862
863 debug("[ pckbc: read from port %i, PS2_STATUS: "
864 "0x%llx ]\n", port_nr, (long long)odata);
865 } else {
866 debug("[ pckbc: write to port %i, PS2_STATUS: "
867 "0x%llx ]\n", port_nr, (long long)idata);
868 }
869 break;
870
871 default:
872 if (writeflag==MEM_READ) {
873 debug("[ pckbc: read from unimplemented reg %i ]\n",
874 (int)relative_addr);
875 odata = d->reg[relative_addr];
876 } else {
877 debug("[ pckbc: write to unimplemented reg %i:",
878 (int)relative_addr);
879 for (i=0; i<len; i++)
880 debug(" %02x", data[i]);
881 debug(" ]\n");
882 d->reg[relative_addr] = idata;
883 }
884 }
885
886 /* SGI? TODO: fix */
887 if (len == 8)
888 odata |= (odata << 8) | (odata << 16) | (odata << 24) |
889 (odata << 32) | (odata << 40) | (odata << 48) |
890 (odata << 56);
891
892 if (writeflag == MEM_READ)
893 memory_writemax64(cpu, data, len, odata);
894
895 dev_pckbc_tick(cpu, d);
896
897 return 1;
898 }
899
900
901 /*
902 * dev_pckbc_init():
903 *
904 * Type should be PCKBC_8042 or PCKBC_8242.
905 */
906 int dev_pckbc_init(struct machine *machine, struct memory *mem,
907 uint64_t baseaddr, int type, char *keyboard_irqpath,
908 char *mouse_irqpath, int in_use, int pc_style_flag)
909 {
910 struct pckbc_data *d;
911 int len = DEV_PCKBC_LENGTH;
912
913 CHECK_ALLOCATION(d = malloc(sizeof(struct pckbc_data)));
914 memset(d, 0, sizeof(struct pckbc_data));
915
916 if (type == PCKBC_8242)
917 len = 0x18;
918
919 if (type == PCKBC_JAZZ) {
920 type = PCKBC_8042;
921 len = DEV_PCKBC_LENGTH + 0x60;
922 }
923
924 INTERRUPT_CONNECT(keyboard_irqpath, d->irq_keyboard);
925 INTERRUPT_CONNECT(mouse_irqpath, d->irq_mouse);
926
927 d->type = type;
928 d->in_use = in_use;
929 d->pc_style_flag = pc_style_flag;
930 d->translation_table = 2;
931 d->rx_int_enable = 1;
932 d->output_byte = 0x02; /* A20 enable on PCs */
933
934 d->console_handle = console_start_slave_inputonly(
935 machine, "pckbc", d->in_use);
936
937 memory_device_register(mem, "pckbc", baseaddr,
938 len, dev_pckbc_access, d, DM_DEFAULT, NULL);
939 machine_add_tickfunction(machine, dev_pckbc_tick, d,
940 PCKBC_TICKSHIFT);
941
942 return d->console_handle;
943 }
944

  ViewVC Help
Powered by ViewVC 1.1.26