/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (hide annotations)
Mon Oct 8 16:18:38 2007 UTC (12 years, 4 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 dpavlin 4 /*
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 dpavlin 12 * $Id: dev_pckbc.c,v 1.48 2005/08/16 06:49:27 debug Exp $
29 dpavlin 4 *
30 dpavlin 6 * Standard 8042 PC keyboard controller (and a 8242WB PS2 keyboard/mouse
31     * controller), including the 8048 keyboard chip.
32 dpavlin 4 *
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 dpavlin 12 /* #define debug fatal */
53 dpavlin 4
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 dpavlin 6 int pc_style_flag;
79 dpavlin 4
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 dpavlin 6 int output_byte;
89 dpavlin 4 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 dpavlin 6 #define STATE_LDOUTPUT 4
100     #define STATE_RDOUTPUT 5
101 dpavlin 4
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 dpavlin 6 else
129     d->tail[port] = (d->tail[port]+1) % MAX_8042_QUEUELEN;
130 dpavlin 4 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 dpavlin 6 int old_head;
143 dpavlin 4 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 dpavlin 6 * 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 dpavlin 4 */
166    
167 dpavlin 6 old_head = d->head[p];
168    
169 dpavlin 4 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 dpavlin 6 /* 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 dpavlin 4 /* 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 dpavlin 6 int port_nr, ch, ints_enabled;
293 dpavlin 4
294 dpavlin 6 if (d->in_use && console_charavail(d->console_handle)) {
295 dpavlin 4 ch = console_readchar(d->console_handle);
296     if (ch >= 0)
297     ascii_to_pc_scancodes(ch, d);
298     }
299    
300 dpavlin 6 ints_enabled = d->rx_int_enable;
301    
302 dpavlin 4 /* TODO: mouse movements? */
303    
304 dpavlin 6 if (d->cmdbyte & KC8_KDISABLE)
305     ints_enabled = 0;
306    
307 dpavlin 4 for (port_nr=0; port_nr<2; port_nr++) {
308 dpavlin 6 /* 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 dpavlin 12 debug("[ pckbc: interrupt port %i ]\n", port_nr);
312 dpavlin 4 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 dpavlin 6 *
325     * Handle commands to the 8048 in the emulated keyboard.
326 dpavlin 4 */
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 dpavlin 6 fatal("[ pckbc: UNIMPLEMENTED 8048 command 0x%02x ]\n", cmd);
371 dpavlin 4 }
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 dpavlin 6 if (relative_addr >= 0x60) {
399 dpavlin 4 relative_addr -= 0x60;
400 dpavlin 6 if (relative_addr != 0)
401     relative_addr = 1;
402     } else if (d->type == PCKBC_8242) {
403     /* 8242 PS2-style: */
404 dpavlin 4 /* 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 dpavlin 6 } 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 dpavlin 4 } else {
428 dpavlin 6 /* Others... Non-Jazz ARC-based machines etc. */
429     if (relative_addr != 0)
430 dpavlin 4 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 dpavlin 6 switch (d->state) {
442     case STATE_RDCMDBYTE:
443 dpavlin 4 odata = d->cmdbyte;
444     d->state = STATE_NORMAL;
445 dpavlin 6 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 dpavlin 4 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 dpavlin 6 /* debug("[ pckbc: read from DATA: 0x%02x ]\n",
459     odata); */
460 dpavlin 4 } else {
461     debug("[ pckbc: write to DATA:");
462     for (i=0; i<len; i++)
463     debug(" %02x", data[i]);
464     debug(" ]\n");
465    
466 dpavlin 6 switch (d->state) {
467     case STATE_LDCMDBYTE:
468 dpavlin 4 d->cmdbyte = idata;
469     d->rx_int_enable = d->cmdbyte &
470     (KC8_KENABLE | KC8_MENABLE) ? 1 : 0;
471     d->state = STATE_NORMAL;
472 dpavlin 6 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 dpavlin 4 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 dpavlin 6 d->state == STATE_RDCMDBYTE ||
491     d->state == STATE_RDOUTPUT)
492 dpavlin 4 odata |= KBS_DIB;
493 dpavlin 6
494     if (d->state == STATE_RDCMDBYTE)
495     odata |= KBS_OCMD;
496    
497     odata |= KBS_NOSEC;
498 dpavlin 4 /* 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 dpavlin 6 case 0xa7:
515     d->cmdbyte |= KC8_MDISABLE;
516     break;
517     case 0xa8:
518     d->cmdbyte &= ~KC8_MDISABLE;
519     break;
520 dpavlin 4 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 dpavlin 6 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 dpavlin 4 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 dpavlin 6 int in_use, int pc_style_flag)
709 dpavlin 4 {
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 dpavlin 12 if (type == PCKBC_8242)
721     len = 0x40;
722    
723 dpavlin 4 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 dpavlin 6 d->pc_style_flag = pc_style_flag;
733 dpavlin 4 d->console_handle = console_start_slave_inputonly(machine, "pckbc");
734 dpavlin 6 d->rx_int_enable = 1;
735     d->output_byte = 0x02; /* A20 enable on PCs */
736 dpavlin 4
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