/[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 12 - (show annotations)
Mon Oct 8 16:18:38 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 19878 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.905 2005/08/16 09:16:24 debug Exp $
20050628	Continuing the work on the ARM translation engine. end_of_page
		works. Experimenting with load/store translation caches
		(virtual -> physical -> host).
20050629	More ARM stuff (memory access translation cache, mostly). This
		might break a lot of stuff elsewhere, probably some MIPS-
		related translation things.
20050630	Many load/stores are now automatically generated and included
		into cpu_arm_instr.c; 1024 functions in total (!).
		Fixes based on feedback from Alec Voropay: only print 8 hex
		digits instead of 16 in some cases when emulating 32-bit
		machines; similar 8 vs 16 digit fix for breakpoint addresses;
		4Kc has 16 TLB entries, not 48; the MIPS config select1
		register is now printed with "reg ,0".
		Also changing many other occurances of 16 vs 8 digit output.
		Adding cache associativity fields to mips_cpu_types.h; updating
		some other cache fields; making the output of
		mips_cpu_dumpinfo() look nicer.
		Generalizing the bintrans stuff for device accesses to also
		work with the new translation system. (This might also break
		some MIPS things.)
		Adding multi-load/store instructions to the ARM disassembler
		and the translator, and some optimizations of various kinds.
20050701	Adding a simple dev_disk (it can read/write sectors from
		disk images).
20050712	Adding dev_ether (a simple ethernet send/receive device).
		Debugger command "ninstrs" for toggling show_nr_of_instructions
		during runtime.
		Removing the framebuffer logo.
20050713	Continuing on dev_ether.
		Adding a dummy cpu_alpha (again).
20050714	More work on cpu_alpha.
20050715	More work on cpu_alpha. Many instructions work, enough to run
		a simple framebuffer fill test (similar to the ARM test).
20050716	More Alpha stuff.
20050717	Minor updates (Alpha stuff).
20050718	Minor updates (Alpha stuff).
20050719	Generalizing some Alpha instructions.
20050720	More Alpha-related updates.
20050721	Continuing on cpu_alpha. Importing rpb.h from NetBSD/alpha.
20050722	Alpha-related updates: userland stuff (Hello World using
		write() compiled statically for FreeBSD/Alpha runs fine), and
		more instructions are now implemented.
20050723	Fixing ldq_u and stq_u.
		Adding more instructions (conditional moves, masks, extracts,
		shifts).
20050724	More FreeBSD/Alpha userland stuff, and adding some more
		instructions (inserts).
20050725	Continuing on the Alpha stuff. (Adding dummy ldt/stt.)
		Adding a -A command line option to turn off alignment checks
		in some cases (for translated code).
		Trying to remove the old bintrans code which updated the pc
		and nr_of_executed_instructions for every instruction.
20050726	Making another attempt att removing the pc/nr of instructions
		code. This time it worked, huge performance increase for
		artificial test code, but performance loss for real-world
		code :-( so I'm scrapping that code for now.
		Tiny performance increase on Alpha (by using ret instead of
		jmp, to play nice with the Alpha's branch prediction) for the
		old MIPS bintrans backend.
20050727	Various minor fixes and cleanups.
20050728	Switching from a 2-level virtual to host/physical translation
		system for ARM emulation, to a 1-level translation.
		Trying to switch from 2-level to 1-level for the MIPS bintrans
		system as well (Alpha only, so far), but there is at least one
		problem: caches and/or how they work with device mappings.
20050730	Doing the 2-level to 1-level conversion for the i386 backend.
		The cache/device bug is still there for R2K/3K :(
		Various other minor updates (Malta etc).
		The mc146818 clock now updates the UIP bit in a way which works
		better with Linux for at least sgimips and Malta emulation.
		Beginning the work on refactoring the dyntrans system.
20050731	Continuing the dyntrans refactoring.
		Fixing a small but serious host alignment bug in memory_rw.
		Adding support for big-endian load/stores to the i386 bintrans
		backend.
		Another minor i386 bintrans backend update: stores from the
		zero register are now one (or two) loads shorter.
		The slt and sltu instructions were incorrectly implemented for
		the i386 backend; only using them for 32-bit mode for now.
20050801	Continuing the dyntrans refactoring.
		Cleanup of the ns16550 serial controller (removing unnecessary
		code).
		Bugfix (memory corruption bug) in dev_gt, and a patch/hack from
		Alec Voropay for Linux/Malta.
20050802	More cleanup/refactoring of the dyntrans subsystem: adding
		phys_page pointers to the lookup tables, for quick jumps
		between translated pages.
		Better fix for the ns16550 device (but still no real FIFO
		functionality).
		Converting cpu_ppc to the new dyntrans system. This means that
		I will have to start from scratch with implementing each
		instruction, and figure out how to implement dual 64/32-bit
		modes etc.
		Removing the URISC CPU family, because it was useless.
20050803	When selecting a machine type, the main type can now be omitted
		if the subtype name is unique. (I.e. -E can be omitted.)
		Fixing a dyntrans/device update bug. (Writes to offset 0 of
		a device could sometimes go unnoticed.)
		Adding an experimental "instruction combination" hack for
		ARM for memset-like byte fill loops.
20050804	Minor progress on cpu_alpha and related things.
		Finally fixing the MIPS dmult/dmultu bugs.
		Fixing some minor TODOs.
20050805	Generalizing the 8259 PIC. It now also works with Cobalt
		and evbmips emulation, in addition to the x86 hack.
		Finally converting the ns16550 device to use devinit.
		Continuing the work on the dyntrans system. Thinking about
		how to add breakpoints.
20050806	More dyntrans updates. Breakpoints seem to work now.
20050807	Minor updates: cpu_alpha and related things; removing
		dev_malta (as it isn't used any more).
		Dyntrans: working on general "show trace tree" support.
		The trace tree stuff now works with both the old MIPS code and
		with newer dyntrans modes. :)
		Continuing on Alpha-related stuff (trying to get *BSD to boot
		a bit further, adding more instructions, etc).
20050808	Adding a dummy IA64 cpu family, and continuing the refactoring
		of the dyntrans system.
		Removing the regression test stuff, because it was more or
		less useless.
		Adding loadlinked/storeconditional type instructions to the
		Alpha emulation. (Needed for Linux/alpha. Not very well tested
		yet.)
20050809	The function call trace tree now prints a per-function nr of
		arguments. (Semi-meaningless, since that data isn't read yet
		from the ELFs; some hardcoded symbols such as memcpy() and
		strlen() work fine, though.)
		More dyntrans refactoring; taking out more of the things that
		are common to all cpu families.
20050810	Working on adding support for "dual mode" for PPC dyntrans
		(i.e. both 64-bit and 32-bit modes).
		(Re)adding some simple PPC instructions.
20050811	Adding a dummy M68K cpu family. The dyntrans system isn't ready
		for variable-length ISAs yet, so it's completely bogus so far.
		Re-adding more PPC instructions.
		Adding a hack to src/file.c which allows OpenBSD/mac68k a.out
		kernels to be loaded.
		Beginning to add PPC loads/stores. So far they only work in
		32-bit mode.
20050812	The configure file option "add_remote" now accepts symbolic
		host names, in addition to numeric IPv4 addresses.
		Re-adding more PPC instructions.
20050814	Continuing to port back more PPC instructions.
		Found and fixed the cache/device write-update bug for 32-bit
		MIPS bintrans. :-)
		Triggered a really weird and annoying bug in Compaq's C
		compiler; ccc sometimes outputs code which loads from an
		address _before_ checking whether the pointer was NULL or not.
		(I'm not sure how to handle this problem.)
20050815	Removing all of the old x86 instruction execution code; adding
		a new (dummy) dyntrans module for x86.
		Taking the first steps to extend the dyntrans system to support
		variable-length instructions.
		Slowly preparing for the next release.
20050816	Adding a dummy SPARC cpu module.
		Minor updates (documentation etc) for the release.

==============  RELEASE 0.3.5  ==============


1 /*
2 * Copyright (C) 2003-2005 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.48 2005/08/16 06:49:27 debug Exp $
29 *
30 * Standard 8042 PC keyboard controller (and a 8242WB PS2 keyboard/mouse
31 * controller), including the 8048 keyboard chip.
32 *
33 *
34 * TODO: Finish the rewrite for 8242.
35 */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include "console.h"
42 #include "cpu.h"
43 #include "devices.h"
44 #include "machine.h"
45 #include "memory.h"
46 #include "misc.h"
47
48 #include "kbdreg.h"
49
50
51 /* #define PCKBC_DEBUG */
52 /* #define debug fatal */
53
54
55 #define MAX_8042_QUEUELEN 256
56
57 #define PC_DATA 0
58 #define PC_CMD 0
59 #define PC_STATUS 1
60
61 #define PS2_TXBUF 0
62 #define PS2_RXBUF 1
63 #define PS2_CONTROL 2
64 #define PS2_STATUS 3
65
66 #define PS2 100
67
68 #define PCKBC_TICKSHIFT 14
69
70 struct pckbc_data {
71 int console_handle;
72 int in_use;
73
74 int reg[DEV_PCKBC_LENGTH];
75 int keyboard_irqnr;
76 int mouse_irqnr;
77 int type;
78 int pc_style_flag;
79
80 /* TODO: one of these for each port? */
81 int clocksignal;
82 int rx_int_enable;
83 int tx_int_enable;
84
85 int keyscanning_enabled;
86 int state;
87 int cmdbyte;
88 int output_byte;
89 int last_scancode;
90
91 unsigned key_queue[2][MAX_8042_QUEUELEN];
92 int head[2], tail[2];
93 };
94
95 #define STATE_NORMAL 0
96 #define STATE_LDCMDBYTE 1
97 #define STATE_RDCMDBYTE 2
98 #define STATE_WAITING_FOR_TRANSLTABLE 3
99 #define STATE_LDOUTPUT 4
100 #define STATE_RDOUTPUT 5
101
102
103 /*
104 * pckbc_add_code():
105 *
106 * Adds a byte to the data queue.
107 */
108 void pckbc_add_code(struct pckbc_data *d, int code, int port)
109 {
110 /* Add at the head, read at the tail: */
111 d->head[port] = (d->head[port]+1) % MAX_8042_QUEUELEN;
112 if (d->head[port] == d->tail[port])
113 fatal("[ pckbc: queue overrun, port %i! ]\n", port);
114
115 d->key_queue[port][d->head[port]] = code;
116 }
117
118
119 /*
120 * pckbc_get_code():
121 *
122 * Reads a byte from a data queue.
123 */
124 int pckbc_get_code(struct pckbc_data *d, int port)
125 {
126 if (d->head[port] == d->tail[port])
127 fatal("[ pckbc: queue empty, port %i! ]\n", port);
128 else
129 d->tail[port] = (d->tail[port]+1) % MAX_8042_QUEUELEN;
130 return d->key_queue[port][d->tail[port]];
131 }
132
133
134 /*
135 * ascii_to_scancodes():
136 *
137 * Conversion from ASCII codes to default (US) keyboard scancodes.
138 * (See http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html)
139 */
140 static void ascii_to_pc_scancodes(int a, struct pckbc_data *d)
141 {
142 int old_head;
143 int p = 0; /* port */
144 int shift = 0, ctrl = 0;
145
146 if (a >= 'A' && a <= 'Z') { a += 32; shift = 1; }
147 if ((a >= 1 && a <= 26) && (a!='\n' && a!='\t' && a!='\b' && a!='\r'))
148 { a += 96; ctrl = 1; }
149
150 if (shift)
151 pckbc_add_code(d, 0x2a, p);
152 else
153 pckbc_add_code(d, 0x2a + 0x80, p);
154
155 if (ctrl)
156 pckbc_add_code(d, 0x1d, p);
157
158 /*
159 * Note: The ugly hack used to add release codes for all of these
160 * keys is as follows: we remember how much of the kbd buf that
161 * is in use here, before we add any scancode. After we've added
162 * one or more scancodes (ie an optional shift + another key)
163 * then we duplicate the last scancode | 0x80 _if_ the kbd buf
164 * was altered.
165 */
166
167 old_head = d->head[p];
168
169 if (a==27) pckbc_add_code(d, 0x01, p);
170
171 if (a=='1') pckbc_add_code(d, 0x02, p);
172 if (a=='2') pckbc_add_code(d, 0x03, p);
173 if (a=='3') pckbc_add_code(d, 0x04, p);
174 if (a=='4') pckbc_add_code(d, 0x05, p);
175 if (a=='5') pckbc_add_code(d, 0x06, p);
176 if (a=='6') pckbc_add_code(d, 0x07, p);
177 if (a=='7') pckbc_add_code(d, 0x08, p);
178 if (a=='8') pckbc_add_code(d, 0x09, p);
179 if (a=='9') pckbc_add_code(d, 0x0a, p);
180 if (a=='0') pckbc_add_code(d, 0x0b, p);
181 if (a=='-') pckbc_add_code(d, 0x0c, p);
182 if (a=='=') pckbc_add_code(d, 0x0d, p);
183
184 if (a=='!') { pckbc_add_code(d, 0x2a, p);
185 pckbc_add_code(d, 0x02, p); }
186 if (a=='@') { pckbc_add_code(d, 0x2a, p);
187 pckbc_add_code(d, 0x03, p); }
188 if (a=='#') { pckbc_add_code(d, 0x2a, p);
189 pckbc_add_code(d, 0x04, p); }
190 if (a=='$') { pckbc_add_code(d, 0x2a, p);
191 pckbc_add_code(d, 0x05, p); }
192 if (a=='%') { pckbc_add_code(d, 0x2a, p);
193 pckbc_add_code(d, 0x06, p); }
194 if (a=='^') { pckbc_add_code(d, 0x2a, p);
195 pckbc_add_code(d, 0x07, p); }
196 if (a=='&') { pckbc_add_code(d, 0x2a, p);
197 pckbc_add_code(d, 0x08, p); }
198 if (a=='*') { pckbc_add_code(d, 0x2a, p);
199 pckbc_add_code(d, 0x09, p); }
200 if (a=='(') { pckbc_add_code(d, 0x2a, p);
201 pckbc_add_code(d, 0x0a, p); }
202 if (a==')') { pckbc_add_code(d, 0x2a, p);
203 pckbc_add_code(d, 0x0b, p); }
204 if (a=='_') { pckbc_add_code(d, 0x2a, p);
205 pckbc_add_code(d, 0x0c, p); }
206 if (a=='+') { pckbc_add_code(d, 0x2a, p);
207 pckbc_add_code(d, 0x0d, p); }
208
209 if (a=='\b') pckbc_add_code(d, 0x0e, p);
210
211 if (a=='\t') pckbc_add_code(d, 0x0f, p);
212 if (a=='q') pckbc_add_code(d, 0x10, p);
213 if (a=='w') pckbc_add_code(d, 0x11, p);
214 if (a=='e') pckbc_add_code(d, 0x12, p);
215 if (a=='r') pckbc_add_code(d, 0x13, p);
216 if (a=='t') pckbc_add_code(d, 0x14, p);
217 if (a=='y') pckbc_add_code(d, 0x15, p);
218 if (a=='u') pckbc_add_code(d, 0x16, p);
219 if (a=='i') pckbc_add_code(d, 0x17, p);
220 if (a=='o') pckbc_add_code(d, 0x18, p);
221 if (a=='p') pckbc_add_code(d, 0x19, p);
222
223 if (a=='[') pckbc_add_code(d, 0x1a, p);
224 if (a=='{') { pckbc_add_code(d, 0x2a, p);
225 pckbc_add_code(d, 0x1a, p); }
226 if (a==']') pckbc_add_code(d, 0x1b, p);
227 if (a=='}') { pckbc_add_code(d, 0x2a, p);
228 pckbc_add_code(d, 0x1b, p); }
229
230 if (a=='\n' || a=='\r') pckbc_add_code(d, 0x1c, p);
231
232 if (a=='a') pckbc_add_code(d, 0x1e, p);
233 if (a=='s') pckbc_add_code(d, 0x1f, p);
234 if (a=='d') pckbc_add_code(d, 0x20, p);
235 if (a=='f') pckbc_add_code(d, 0x21, p);
236 if (a=='g') pckbc_add_code(d, 0x22, p);
237 if (a=='h') pckbc_add_code(d, 0x23, p);
238 if (a=='j') pckbc_add_code(d, 0x24, p);
239 if (a=='k') pckbc_add_code(d, 0x25, p);
240 if (a=='l') pckbc_add_code(d, 0x26, p);
241
242 if (a==';') pckbc_add_code(d, 0x27, p);
243 if (a==':') { pckbc_add_code(d, 0x2a, p);
244 pckbc_add_code(d, 0x27, p); }
245 if (a=='\'') pckbc_add_code(d, 0x28, p);
246 if (a=='"') { pckbc_add_code(d, 0x2a, p);
247 pckbc_add_code(d, 0x28, p); }
248 if (a=='~') pckbc_add_code(d, 0x29, p);
249
250 if (a=='\\') pckbc_add_code(d, 0x2b, p);
251 if (a=='|') { pckbc_add_code(d, 0x2a, p);
252 pckbc_add_code(d, 0x2b, p); }
253
254 if (a=='z') pckbc_add_code(d, 0x2c, p);
255 if (a=='x') pckbc_add_code(d, 0x2d, p);
256 if (a=='c') pckbc_add_code(d, 0x2e, p);
257 if (a=='v') pckbc_add_code(d, 0x2f, p);
258 if (a=='b') pckbc_add_code(d, 0x30, p);
259 if (a=='n') pckbc_add_code(d, 0x31, p);
260 if (a=='m') pckbc_add_code(d, 0x32, p);
261
262 if (a==',') pckbc_add_code(d, 0x33, p);
263 if (a=='<') { pckbc_add_code(d, 0x2a, p);
264 pckbc_add_code(d, 0x33, p); }
265 if (a=='.') pckbc_add_code(d, 0x34, p);
266 if (a=='>') { pckbc_add_code(d, 0x2a, p);
267 pckbc_add_code(d, 0x34, p); }
268 if (a=='/') pckbc_add_code(d, 0x35, p);
269 if (a=='?') { pckbc_add_code(d, 0x2a, p);
270 pckbc_add_code(d, 0x35, p); }
271
272 if (a==' ') pckbc_add_code(d, 0x39, p);
273
274 /* Add release code, if a key was pressed: */
275 if (d->head[p] != old_head) {
276 int code = d->key_queue[p][d->head[p]] | 0x80;
277 pckbc_add_code(d, code, p);
278 }
279
280 /* Release ctrl: */
281 if (ctrl)
282 pckbc_add_code(d, 0x1d + 0x80, p);
283 }
284
285
286 /*
287 * dev_pckbc_tick():
288 */
289 void dev_pckbc_tick(struct cpu *cpu, void *extra)
290 {
291 struct pckbc_data *d = extra;
292 int port_nr, ch, ints_enabled;
293
294 if (d->in_use && console_charavail(d->console_handle)) {
295 ch = console_readchar(d->console_handle);
296 if (ch >= 0)
297 ascii_to_pc_scancodes(ch, d);
298 }
299
300 ints_enabled = d->rx_int_enable;
301
302 /* TODO: mouse movements? */
303
304 if (d->cmdbyte & KC8_KDISABLE)
305 ints_enabled = 0;
306
307 for (port_nr=0; port_nr<2; port_nr++) {
308 /* Cause receive interrupt, if there's something in the
309 receive buffer: (Otherwise deassert the interrupt.) */
310 if (d->head[port_nr] != d->tail[port_nr] && ints_enabled) {
311 debug("[ pckbc: interrupt port %i ]\n", port_nr);
312 cpu_interrupt(cpu, port_nr==0? d->keyboard_irqnr
313 : d->mouse_irqnr);
314 } else {
315 cpu_interrupt_ack(cpu, port_nr==0? d->keyboard_irqnr
316 : d->mouse_irqnr);
317 }
318 }
319 }
320
321
322 /*
323 * dev_pckbc_command():
324 *
325 * Handle commands to the 8048 in the emulated keyboard.
326 */
327 static void dev_pckbc_command(struct pckbc_data *d, int port_nr)
328 {
329 int cmd = d->reg[PC_CMD];
330
331 if (d->type == PCKBC_8242)
332 cmd = d->reg[PS2_TXBUF];
333
334 if (d->state == STATE_WAITING_FOR_TRANSLTABLE) {
335 debug("[ pckbc: switching to translation table 0x%02x ]\n",
336 cmd);
337 pckbc_add_code(d, KBR_ACK, port_nr);
338 d->state = STATE_NORMAL;
339 return;
340 }
341
342 switch (cmd) {
343 case 0x00:
344 pckbc_add_code(d, KBR_ACK, port_nr);
345 break;
346 case KBC_MODEIND: /* Set LEDs */
347 /* Just ACK, no LEDs are actually set. */
348 pckbc_add_code(d, KBR_ACK, port_nr);
349 break;
350 case KBC_SETTABLE:
351 pckbc_add_code(d, KBR_ACK, port_nr);
352 d->state = STATE_WAITING_FOR_TRANSLTABLE;
353 break;
354 case KBC_ENABLE:
355 d->keyscanning_enabled = 1;
356 pckbc_add_code(d, KBR_ACK, port_nr);
357 break;
358 case KBC_DISABLE:
359 d->keyscanning_enabled = 0;
360 pckbc_add_code(d, KBR_ACK, port_nr);
361 break;
362 case KBC_SETDEFAULT:
363 pckbc_add_code(d, KBR_ACK, port_nr);
364 break;
365 case KBC_RESET:
366 pckbc_add_code(d, KBR_ACK, port_nr);
367 pckbc_add_code(d, KBR_RSTDONE, port_nr);
368 break;
369 default:
370 fatal("[ pckbc: UNIMPLEMENTED 8048 command 0x%02x ]\n", cmd);
371 }
372 }
373
374
375 /*
376 * dev_pckbc_access():
377 */
378 int dev_pckbc_access(struct cpu *cpu, struct memory *mem,
379 uint64_t relative_addr, unsigned char *data, size_t len,
380 int writeflag, void *extra)
381 {
382 uint64_t idata = 0, odata = 0;
383 int i, port_nr = 0;
384 struct pckbc_data *d = extra;
385
386 idata = memory_readmax64(cpu, data, len);
387
388 #ifdef PCKBC_DEBUG
389 if (writeflag == MEM_WRITE)
390 fatal("[ pckbc: write to addr 0x%x: 0x%x ]\n",
391 (int)relative_addr, (int)idata);
392 else
393 fatal("[ pckbc: read from addr 0x%x ]\n",
394 (int)relative_addr);
395 #endif
396
397 /* For JAZZ-based machines: */
398 if (relative_addr >= 0x60) {
399 relative_addr -= 0x60;
400 if (relative_addr != 0)
401 relative_addr = 1;
402 } else if (d->type == PCKBC_8242) {
403 /* 8242 PS2-style: */
404 /* when using 8-byte alignment... */
405 relative_addr /= sizeof(uint64_t);
406 /* port_nr = 0 for keyboard, 1 for mouse */
407 port_nr = (relative_addr >> 2);
408 relative_addr &= 3;
409 relative_addr += PS2;
410 } else if (d->pc_style_flag) {
411 /* PC-style: */
412 if (relative_addr != 0 && relative_addr != 4) {
413 /* TODO (port 0x61) */
414 odata = 0x21;
415 {
416 static int x = 0;
417 x++;
418 if (x&1)
419 odata ^= 0x10;
420 }
421 if (writeflag == MEM_READ)
422 memory_writemax64(cpu, data, len, odata);
423 return 0;
424 }
425 if (relative_addr != 0)
426 relative_addr = 1;
427 } else {
428 /* Others... Non-Jazz ARC-based machines etc. */
429 if (relative_addr != 0)
430 relative_addr = 1;
431 }
432
433 switch (relative_addr) {
434
435 /*
436 * 8042 (PC):
437 */
438
439 case 0: /* data */
440 if (writeflag==MEM_READ) {
441 switch (d->state) {
442 case STATE_RDCMDBYTE:
443 odata = d->cmdbyte;
444 d->state = STATE_NORMAL;
445 break;
446 case STATE_RDOUTPUT:
447 odata = d->output_byte;
448 d->state = STATE_NORMAL;
449 break;
450 default:if (d->head[0] != d->tail[0]) {
451 odata = pckbc_get_code(d, 0);
452 d->last_scancode = odata;
453 } else {
454 odata = d->last_scancode;
455 d->last_scancode |= 0x80;
456 }
457 }
458 /* debug("[ pckbc: read from DATA: 0x%02x ]\n",
459 odata); */
460 } else {
461 debug("[ pckbc: write to DATA:");
462 for (i=0; i<len; i++)
463 debug(" %02x", data[i]);
464 debug(" ]\n");
465
466 switch (d->state) {
467 case STATE_LDCMDBYTE:
468 d->cmdbyte = idata;
469 d->rx_int_enable = d->cmdbyte &
470 (KC8_KENABLE | KC8_MENABLE) ? 1 : 0;
471 d->state = STATE_NORMAL;
472 break;
473 case STATE_LDOUTPUT:
474 d->output_byte = idata;
475 d->state = STATE_NORMAL;
476 break;
477 default:d->reg[relative_addr] = idata;
478 dev_pckbc_command(d, port_nr);
479 }
480 }
481 break;
482 case 1: /* control */
483 if (writeflag==MEM_READ) {
484 dev_pckbc_tick(cpu, d);
485
486 odata = 0;
487
488 /* "Data in buffer" bit */
489 if (d->head[0] != d->tail[0] ||
490 d->state == STATE_RDCMDBYTE ||
491 d->state == STATE_RDOUTPUT)
492 odata |= KBS_DIB;
493
494 if (d->state == STATE_RDCMDBYTE)
495 odata |= KBS_OCMD;
496
497 odata |= KBS_NOSEC;
498 /* debug("[ pckbc: read from CTL status port: "
499 "0x%02x ]\n", (int)odata); */
500 } else {
501 debug("[ pckbc: write to CTL:");
502 for (i=0; i<len; i++)
503 debug(" %02x", data[i]);
504 debug(" ]\n");
505 d->reg[relative_addr] = idata;
506
507 switch (idata) {
508 case K_RDCMDBYTE:
509 d->state = STATE_RDCMDBYTE;
510 break;
511 case K_LDCMDBYTE:
512 d->state = STATE_LDCMDBYTE;
513 break;
514 case 0xa7:
515 d->cmdbyte |= KC8_MDISABLE;
516 break;
517 case 0xa8:
518 d->cmdbyte &= ~KC8_MDISABLE;
519 break;
520 case 0xa9: /* test auxiliary port */
521 debug("[ pckbc: CONTROL 0xa9, TODO ]\n");
522 break;
523 case 0xaa: /* keyboard self-test */
524 pckbc_add_code(d, 0x55, port_nr);
525 break;
526 case 0xad:
527 d->cmdbyte |= KC8_KDISABLE;
528 break;
529 case 0xae:
530 d->cmdbyte &= ~KC8_KDISABLE;
531 break;
532 case 0xd0:
533 d->state = STATE_RDOUTPUT;
534 break;
535 case 0xd1:
536 d->state = STATE_LDOUTPUT;
537 break;
538 case 0xd4: /* write to auxiliary port */
539 debug("[ pckbc: CONTROL 0xd4, TODO ]\n");
540 break;
541 default:
542 fatal("[ pckbc: unknown CONTROL 0x%x ]\n",
543 idata);
544 d->state = STATE_NORMAL;
545 }
546 }
547 break;
548
549 /*
550 * 8242 (PS2):
551 */
552
553 /*
554 * BIG TODO: The following should be rewritten to use dev_pckbc_command()
555 * etc, like the 8042 code above does.
556 */
557
558 case PS2 + PS2_TXBUF:
559 if (writeflag==MEM_READ) {
560 odata = random() & 0xff;
561 debug("[ pckbc: read from port %i, PS2_TXBUF: "
562 "0x%x ]\n", port_nr, (int)odata);
563 } else {
564 debug("[ pckbc: write to port %i, PS2_TXBUF: "
565 "0x%llx ]\n", port_nr, (long long)idata);
566
567 /* Handle keyboard commands: */
568 switch (idata) {
569 /* These are incorrect, the second byte of
570 commands should be treated better: */
571 case 0x00: /* second byte of 0xed,
572 SGI-IP32's prom */
573 pckbc_add_code(d, 0x03, port_nr);/* ack (?) */
574 break;
575 case 0x14: /* second byte of 0xfc,
576 SGI-IP32's prom */
577 case 0x28: /* second byte of 0xf3,
578 SGI-IP32's prom */
579 case 0x76: /* third byte of 0xfc,
580 SGI-IP32's prom */
581 case 0x03: /* second byte of
582 ATKBD_CMD_GSCANSET (?) */
583 case 0x04:
584 pckbc_add_code(d, 0x03, port_nr);/* ? */
585 break;
586
587 /* Command bytes: */
588 case 0xf0: /* ATKBD_CMD_GSCANSET (?) */
589 pckbc_add_code(d, 0x03, port_nr);/* ? */
590 break;
591 case 0xf2: /* Get keyboard ID */
592 /* The keyboard should generate 2
593 status bytes. */
594 pckbc_add_code(d, 0xab, port_nr);
595 pckbc_add_code(d, 0x83, port_nr);
596 break;
597 case 0xed: /* "ATKBD_CMD_SETLEDS",
598 takes 1 byte arg */
599 case 0xf3: /* "PSMOUSE_CMD_SETRATE",
600 takes 1 byte arg */
601 case 0xf4: /* "ATKBD_CMD_ENABLE" (or
602 PSMOUSE_CMD_ENABLE), no args */
603 case 0xf5: /* "ATKBD_CMD_RESET_DIS" (keyboard,
604 according to Linux sources) */
605 case 0xf6: /* "PSMOUSE_CMD_RESET_DIS" (mouse,
606 according to Linux sources) */
607 /* TODO: what does this do? */
608 pckbc_add_code(d, 0xfa, port_nr);/* ack (?) */
609 break;
610 case 0xfa: /* "ATKBD_CMD_SETALL_MBR" (linux) */
611 pckbc_add_code(d, 0xfa, port_nr);/* ack (?) */
612 break;
613 case 0xfc: /* ? */
614 pckbc_add_code(d, 0xfa, port_nr);/* ack (?) */
615 break;
616 case 0xff: /* Keyboard reset */
617 /* The keyboard should generate 2
618 status bytes. */
619 pckbc_add_code(d, 0xfa, port_nr);/* ack (?) */
620 pckbc_add_code(d, 0xaa, port_nr);
621 /* battery ok (?) */
622 break;
623 default:
624 debug("[ pckbc: UNIMPLEMENTED keyboard command"
625 " 0x%02x (port %i) ]\n", (int)idata,
626 port_nr);
627 }
628 }
629 break;
630
631 case PS2 + PS2_RXBUF:
632 if (writeflag==MEM_READ) {
633 /* TODO: What should be returned if no data
634 is available? */
635 odata = random() & 0xff;
636 if (d->head[port_nr] != d->tail[port_nr])
637 odata = pckbc_get_code(d, port_nr);
638 debug("[ pckbc: read from port %i, PS2_RXBUF: "
639 "0x%02x ]\n", port_nr, (int)odata);
640 } else {
641 debug("[ pckbc: write to port %i, PS2_RXBUF: "
642 "0x%llx ]\n", port_nr, (long long)idata);
643 }
644 break;
645
646 case PS2 + PS2_CONTROL:
647 if (writeflag==MEM_READ) {
648 debug("[ pckbc: read from port %i, PS2_CONTROL"
649 " ]\n", port_nr);
650 } else {
651 debug("[ pckbc: write to port %i, PS2_CONTROL:"
652 " 0x%llx ]\n", port_nr, (long long)idata);
653 d->clocksignal = (idata & 0x10) ? 1 : 0;
654 d->rx_int_enable = (idata & 0x08) ? 1 : 0;
655 d->tx_int_enable = (idata & 0x04) ? 1 : 0;
656 }
657 break;
658
659 case PS2 + PS2_STATUS:
660 if (writeflag==MEM_READ) {
661 /* 0x08 = transmit buffer empty */
662 odata = d->clocksignal + 0x08;
663
664 if (d->head[port_nr] != d->tail[port_nr]) {
665 /* 0x10 = receicer data available (?) */
666 odata |= 0x10;
667 }
668
669 debug("[ pckbc: read from port %i, PS2_STATUS: "
670 "0x%llx ]\n", port_nr, (long long)odata);
671 } else {
672 debug("[ pckbc: write to port %i, PS2_STATUS: "
673 "0x%llx ]\n", port_nr, (long long)idata);
674 }
675 break;
676
677 default:
678 if (writeflag==MEM_READ) {
679 debug("[ pckbc: read from unimplemented reg %i ]\n",
680 (int)relative_addr);
681 odata = d->reg[relative_addr];
682 } else {
683 debug("[ pckbc: write to unimplemented reg %i:",
684 (int)relative_addr);
685 for (i=0; i<len; i++)
686 debug(" %02x", data[i]);
687 debug(" ]\n");
688 d->reg[relative_addr] = idata;
689 }
690 }
691
692 if (writeflag == MEM_READ)
693 memory_writemax64(cpu, data, len, odata);
694
695 dev_pckbc_tick(cpu, d);
696
697 return 1;
698 }
699
700
701 /*
702 * dev_pckbc_init():
703 *
704 * Type should be PCKBC_8042 or PCKBC_8242.
705 */
706 int dev_pckbc_init(struct machine *machine, struct memory *mem,
707 uint64_t baseaddr, int type, int keyboard_irqnr, int mouse_irqnr,
708 int in_use, int pc_style_flag)
709 {
710 struct pckbc_data *d;
711 int len = DEV_PCKBC_LENGTH;
712
713 d = malloc(sizeof(struct pckbc_data));
714 if (d == NULL) {
715 fprintf(stderr, "out of memory\n");
716 exit(1);
717 }
718 memset(d, 0, sizeof(struct pckbc_data));
719
720 if (type == PCKBC_8242)
721 len = 0x40;
722
723 if (type == PCKBC_JAZZ) {
724 type = PCKBC_8042;
725 len = DEV_PCKBC_LENGTH + 0x60;
726 }
727
728 d->type = type;
729 d->keyboard_irqnr = keyboard_irqnr;
730 d->mouse_irqnr = mouse_irqnr;
731 d->in_use = in_use;
732 d->pc_style_flag = pc_style_flag;
733 d->console_handle = console_start_slave_inputonly(machine, "pckbc");
734 d->rx_int_enable = 1;
735 d->output_byte = 0x02; /* A20 enable on PCs */
736
737 memory_device_register(mem, "pckbc", baseaddr,
738 len, dev_pckbc_access, d, MEM_DEFAULT, NULL);
739 machine_add_tickfunction(machine, dev_pckbc_tick, d, PCKBC_TICKSHIFT);
740
741 return d->console_handle;
742 }
743

  ViewVC Help
Powered by ViewVC 1.1.26