/[gxemul]/trunk/src/devices/dev_scc.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_scc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (show annotations)
Mon Oct 8 16:18:00 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 13182 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.707 2005/04/27 16:37:33 debug Exp $
20050408	Some minor updates to the wdc. Linux now doesn't complain
		anymore if a disk is non-present.
20050409	Various minor fixes (a bintrans bug, and some other things).
		The wdc seems to work with Playstation2 emulation, but there
		is a _long_ annoying delay when disks are detected.
		Fixing a really important bintrans bug (when devices and RAM
		are mixed within 4KB pages), which was triggered with
		NetBSD/playstation2 kernels.
20050410	Adding a dummy dev_ps2_ether (just so that NetBSD doesn't
		complain as much during bootup).
		Symbols starting with '$' are now ignored.
		Renaming dev_ps2_ohci.c to dev_ohci.c, etc.
20050411	Moving the bintrans-cache-isolation check from cpu_mips.c to
		cpu_mips_coproc.c. (I thought this would give a speedup, but
		it's not noticable.)
		Better playstation2 sbus interrupt code.
		Skip ahead many ticks if the count register is read manually.
		(This increases the speed of delay-loops that simply read
		the count register.)
20050412	Updates to the playstation2 timer/interrupt code.
		Some other minor updates.
20050413	NetBSD/cobalt runs from a disk image :-) including userland;
		updating the documentation on how to install NetBSD/cobalt
		using NetBSD/pmax (!).
		Some minor bintrans updates (no real speed improvement) and
		other minor updates (playstation2 now uses the -o options).
20050414	Adding a dummy x86 (and AMD64) mode.
20050415	Adding some (32-bit and 16-bit) x86 instructions.
		Adding some initial support for non-SCSI, non-IDE floppy
		images. (The x86 mode can boot from these, more or less.)
		Moving the devices/ and include/ directories to src/devices/
		and src/include/, respectively.
20050416	Continuing on the x86 stuff. (Adding pc_bios.c and some simple
		support for software interrupts in 16-bit mode.)
20050417	Ripping out most of the x86 instruction decoding stuff, trying
		to rewrite it in a cleaner way.
		Disabling some of the least working CPU families in the
		configure script (sparc, x86, alpha, hppa), so that they are
		not enabled by default.
20050418	Trying to fix the bug which caused problems when turning on
		and off bintrans interactively, by flushing the bintrans cache
		whenever bintrans is manually (re)enabled.
20050419	Adding the 'lswi' ppc instruction.
		Minor updates to the x86 instruction decoding.
20050420	Renaming x86 register name indices from R_xx to X86_R_xx (this
		makes building on Tru64 nicer).
20050422	Adding a check for duplicate MIPS TLB entries on tlbwr/tlbwi.
20050427	Adding screenshots to guestoses.html.
		Some minor fixes and testing for the next release.

==============  RELEASE 0.3.2  ==============


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_scc.c,v 1.27 2005/02/25 06:14:30 debug Exp $
29 *
30 * Serial controller on some DECsystems and SGI machines. (Z8530 ?)
31 * Most of the code in here is written for DECsystem emulation, though.
32 *
33 * NOTE:
34 * Each scc device is responsible for two lines; the first scc device
35 * controls mouse (0) and keyboard (1), and the second device controls
36 * serial ports (2 and 3).
37 *
38 * TODO:
39 * Mouse support!!! (scc0 and scc1 need to cooperate, in order to
40 * emulate the same lk201 behaviour as when using the dc device)
41 * DMA
42 * More correct interrupt support.
43 */
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #include "console.h"
50 #include "cpu.h"
51 #include "devices.h"
52 #include "machine.h"
53 #include "memory.h"
54 #include "misc.h"
55
56 #include "sccreg.h"
57
58
59 #define SCC_TICK_SHIFT 14
60
61 #define N_SCC_PORTS 2
62 #define N_SCC_REGS 16
63 #define MAX_QUEUE_LEN 1024
64
65 /* #define SCC_DEBUG */
66
67
68 struct scc_data {
69 int irq_nr;
70 int use_fb;
71 int console_handle;
72
73 int scc_nr;
74 int addrmul;
75
76 int register_select_in_progress[N_SCC_PORTS];
77 int register_selected[N_SCC_PORTS];
78
79 unsigned char scc_register_r[N_SCC_PORTS * N_SCC_REGS];
80 unsigned char scc_register_w[N_SCC_PORTS * N_SCC_REGS];
81
82 unsigned char rx_queue_char[N_SCC_PORTS * MAX_QUEUE_LEN];
83 int cur_rx_queue_pos_write[N_SCC_PORTS];
84 int cur_rx_queue_pos_read[N_SCC_PORTS];
85
86 struct lk201_data lk201;
87 };
88
89
90 /*
91 * dev_scc_add_to_rx_queue():
92 *
93 * Add a character to the receive queue.
94 */
95 void dev_scc_add_to_rx_queue(void *e, int ch, int portnr)
96 {
97 struct scc_data *d = (struct scc_data *) e;
98 int scc_nr;
99
100 /* DC's keyboard port ==> SCC keyboard port */
101 if (portnr == 0)
102 portnr = 3;
103
104 scc_nr = portnr / N_SCC_PORTS;
105 if (scc_nr != d->scc_nr)
106 return;
107
108 portnr &= (N_SCC_PORTS - 1);
109
110 d->rx_queue_char[portnr * MAX_QUEUE_LEN +
111 d->cur_rx_queue_pos_write[portnr]] = ch;
112 d->cur_rx_queue_pos_write[portnr] ++;
113 if (d->cur_rx_queue_pos_write[portnr] == MAX_QUEUE_LEN)
114 d->cur_rx_queue_pos_write[portnr] = 0;
115
116 if (d->cur_rx_queue_pos_write[portnr] ==
117 d->cur_rx_queue_pos_read[portnr])
118 fatal("warning: add_to_rx_queue(): rx_queue overrun!\n");
119 }
120
121
122 static int rx_avail(struct scc_data *d, int portnr)
123 {
124 return d->cur_rx_queue_pos_write[portnr] !=
125 d->cur_rx_queue_pos_read[portnr];
126 }
127
128
129 static unsigned char rx_nextchar(struct scc_data *d, int portnr)
130 {
131 unsigned char ch;
132 ch = d->rx_queue_char[portnr * MAX_QUEUE_LEN +
133 d->cur_rx_queue_pos_read[portnr]];
134 d->cur_rx_queue_pos_read[portnr]++;
135 if (d->cur_rx_queue_pos_read[portnr] == MAX_QUEUE_LEN)
136 d->cur_rx_queue_pos_read[portnr] = 0;
137 return ch;
138 }
139
140
141 /*
142 * dev_scc_tick():
143 */
144 void dev_scc_tick(struct cpu *cpu, void *extra)
145 {
146 int i;
147 struct scc_data *d = (struct scc_data *) extra;
148
149 /* Add keystrokes to the rx queue: */
150 if (d->use_fb == 0 && d->scc_nr == 1) {
151 if (console_charavail(d->console_handle))
152 dev_scc_add_to_rx_queue(extra, console_readchar(
153 d->console_handle), 2);
154 }
155 if (d->use_fb == 1 && d->scc_nr == 1)
156 lk201_tick(&d->lk201);
157
158 for (i=0; i<N_SCC_PORTS; i++) {
159 d->scc_register_r[i * N_SCC_REGS + SCC_RR0] |= SCC_RR0_TX_EMPTY;
160 d->scc_register_r[i * N_SCC_REGS + SCC_RR1] = 0;
161 /* No receive errors */
162
163 d->scc_register_r[i * N_SCC_REGS + SCC_RR0] &=
164 ~SCC_RR0_RX_AVAIL;
165 if (rx_avail(d, i))
166 d->scc_register_r[i * N_SCC_REGS + SCC_RR0] |=
167 SCC_RR0_RX_AVAIL;
168
169 /*
170 * Interrupts:
171 * (NOTE: Interrupt enables are always at channel A)
172 */
173 if (d->scc_register_w[N_SCC_REGS + SCC_WR9] &
174 SCC_WR9_MASTER_IE) {
175 /* TX interrupts? */
176 if (d->scc_register_w[i * N_SCC_REGS + SCC_WR1] &
177 SCC_WR1_TX_IE) {
178 if (d->scc_register_r[i * N_SCC_REGS + SCC_RR3]
179 & SCC_RR3_TX_IP_A ||
180 d->scc_register_r[i * N_SCC_REGS + SCC_RR3]
181 & SCC_RR3_TX_IP_B)
182 cpu_interrupt(cpu, d->irq_nr);
183 }
184
185 /* RX interrupts? */
186 if (d->scc_register_w[N_SCC_REGS + SCC_WR1] &
187 (SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_RXI_ALL_CHAR)) {
188 if (d->scc_register_r[i * N_SCC_REGS + SCC_RR0]
189 & SCC_RR0_RX_AVAIL) {
190 if (i == SCC_CHANNEL_A)
191 d->scc_register_r[N_SCC_REGS +
192 SCC_RR3] |= SCC_RR3_RX_IP_A;
193 else
194 d->scc_register_r[N_SCC_REGS +
195 SCC_RR3] |= SCC_RR3_RX_IP_B;
196 }
197
198 if (d->scc_register_r[i * N_SCC_REGS + SCC_RR3]
199 & SCC_RR3_RX_IP_A ||
200 d->scc_register_r[i * N_SCC_REGS + SCC_RR3]
201 & SCC_RR3_RX_IP_B)
202 cpu_interrupt(cpu, d->irq_nr);
203 }
204
205 if (d->scc_register_w[N_SCC_REGS + SCC_WR1] &
206 SCC_WR1_DMA_MODE) {
207 if (d->scc_register_r[i * N_SCC_REGS + SCC_RR0]
208 & SCC_RR0_RX_AVAIL) {
209 if (i == SCC_CHANNEL_A)
210 d->scc_register_r[N_SCC_REGS +
211 SCC_RR3] |=
212 SCC_RR3_EXT_IP_A;
213 else
214 d->scc_register_r[N_SCC_REGS +
215 SCC_RR3] |=
216 SCC_RR3_EXT_IP_B;
217 }
218
219 if (d->scc_register_r[i * N_SCC_REGS + SCC_RR3]
220 & SCC_RR3_EXT_IP_A ||
221 d->scc_register_r[i * N_SCC_REGS + SCC_RR3]
222 & SCC_RR3_EXT_IP_B)
223 {
224 cpu_interrupt(cpu, d->irq_nr);
225 /* TODO: huh? */
226 cpu_interrupt(cpu, 8 + 0x02000000);
227 }
228 }
229 }
230 }
231 }
232
233
234 /*
235 * dev_scc_dma_func():
236 */
237 int dev_scc_dma_func(struct cpu *cpu, void *extra, uint64_t addr,
238 size_t dma_len, int tx)
239 {
240 /* printf("dev_scc_dma_func(): addr = %08x, len = %i\n",
241 (int)addr, (int)dma_len); */
242 unsigned char word[4];
243 struct scc_data *d = (struct scc_data *) extra;
244 int n;
245
246 int port = SCC_CHANNEL_A; /* TODO */
247
248 if (tx) {
249 do {
250 cpu->memory_rw(cpu, cpu->mem, addr, &word[0],
251 sizeof(word), MEM_READ, NO_EXCEPTIONS | PHYSICAL);
252
253 lk201_tx_data(&d->lk201, d->scc_nr * 2 + port, word[1]);
254 /* Loopback: */
255 if (d->scc_register_w[port * N_SCC_REGS + SCC_WR14]
256 & SCC_WR14_LOCAL_LOOPB)
257 dev_scc_add_to_rx_queue(d, word[1],
258 d->scc_nr * 2 + port);
259
260 addr += sizeof(word);
261 } while ((addr & 0xffc) != 0);
262
263 dev_scc_tick(cpu, extra);
264 return 1;
265 } else {
266 printf("dev_scc_dma_func(): addr = %08x, len = %i\n",
267 (int)addr, (int)dma_len);
268
269
270 /* TODO: all this is just nonsense */
271
272 n = 0;
273 while (rx_avail(d, port)) {
274 word[0] = word[1] = word[2] = word[3] = 0;
275 word[0] = word[1] = word[2] = word[3] =
276 rx_nextchar(d, port);
277 n++;
278 cpu->memory_rw(cpu, cpu->mem, addr, &word[0],
279 sizeof(word), MEM_WRITE, NO_EXCEPTIONS | PHYSICAL);
280
281 addr += sizeof(word);
282 /* Half-page? */
283 if ((addr & 0x7fc) == 0)
284 break;
285 }
286 dev_scc_tick(cpu, extra);
287 return n*4;
288 }
289 }
290
291
292 /*
293 * dev_scc_access():
294 */
295 int dev_scc_access(struct cpu *cpu, struct memory *mem,
296 uint64_t relative_addr, unsigned char *data, size_t len,
297 int writeflag, void *extra)
298 {
299 struct scc_data *d = (struct scc_data *) extra;
300 uint64_t idata = 0, odata = 0;
301 int port;
302 int ultrix_mode = 0;
303
304 idata = memory_readmax64(cpu, data, len);
305
306 /* relative_addr /= d->addrmul; */
307 /* See SGI comment below instead. */
308 /*
309 * SGI writes command to 0x0f, and data to 0x1f.
310 * (TODO: This works for port nr 0, how about port nr 1?)
311 */
312 if ((relative_addr & 0x0f) == 0xf) {
313 if (relative_addr == 0x0f)
314 relative_addr = 1;
315 else
316 relative_addr = 5;
317 }
318
319 port = relative_addr / 8;
320 relative_addr &= 7;
321
322 dev_scc_tick(cpu, extra);
323
324 /*
325 * Ultrix writes words such as 0x1200 to relative address 0,
326 * instead of writing the byte 0x12 directly to address 1.
327 */
328 if ((relative_addr == 0 || relative_addr == 4) && (idata & 0xff) == 0) {
329 ultrix_mode = 1;
330 relative_addr ++;
331 idata >>= 8;
332 }
333
334 switch (relative_addr) {
335 case 1: /* command */
336 if (writeflag==MEM_READ) {
337 odata = d->scc_register_r[port * N_SCC_REGS +
338 d->register_selected[port]];
339
340 if (d->register_selected[port] == SCC_RR3) {
341 if (port == SCC_CHANNEL_B)
342 fatal("WARNING! scc channel B has "
343 "no RR3\n");
344
345 d->scc_register_r[port * N_SCC_REGS +
346 SCC_RR3] = 0;
347 cpu_interrupt_ack(cpu, d->irq_nr);
348 }
349
350 #ifdef SCC_DEBUG
351 fatal("[ scc: port %i, register %i, read value "
352 "0x%02x ]\n", port, d->register_selected[port],
353 (int)odata);
354 #endif
355 d->register_select_in_progress[port] = 0;
356 d->register_selected[port] = 0;
357 /* debug("[ scc: (port %i) read from 0x%08lx ]\n",
358 port, (long)relative_addr); */
359 } else {
360 /* If no register is selected, then select one.
361 Otherwise, write to the selected register. */
362 if (d->register_select_in_progress[port] == 0) {
363 d->register_select_in_progress[port] = 1;
364 d->register_selected[port] = idata;
365 d->register_selected[port] &= (N_SCC_REGS-1);
366 } else {
367 d->scc_register_w[port * N_SCC_REGS +
368 d->register_selected[port]] = idata;
369 #ifdef SCC_DEBUG
370 fatal("[ scc: port %i, register %i, write "
371 "value 0x%02x ]\n", port,
372 d->register_selected[port], idata);
373 #endif
374
375 d->scc_register_r[port * N_SCC_REGS +
376 SCC_RR12] = d->scc_register_w[port *
377 N_SCC_REGS + SCC_WR12];
378 d->scc_register_r[port * N_SCC_REGS +
379 SCC_RR13] = d->scc_register_w[port *
380 N_SCC_REGS + SCC_WR13];
381
382 d->register_select_in_progress[port] = 0;
383 d->register_selected[port] = 0;
384 }
385 }
386 break;
387 case 5: /* data */
388 if (writeflag==MEM_READ) {
389 if (rx_avail(d, port))
390 odata = rx_nextchar(d, port);
391
392 /* TODO: perhaps only clear the RX part of RR3? */
393 d->scc_register_r[N_SCC_REGS + SCC_RR3] = 0;
394 cpu_interrupt_ack(cpu, d->irq_nr);
395
396 debug("[ scc: (port %i) read from 0x%08lx: 0x%02x ]\n",
397 port, (long)relative_addr, (int)odata);
398 } else {
399 /* debug("[ scc: (port %i) write to 0x%08lx: "
400 "0x%08x ]\n", port, (long)relative_addr,
401 (int)idata); */
402
403 /* Send the character: */
404 lk201_tx_data(&d->lk201, d->scc_nr * 2 + port, idata);
405
406 /* Loopback: */
407 if (d->scc_register_w[port * N_SCC_REGS + SCC_WR14]
408 & SCC_WR14_LOCAL_LOOPB)
409 dev_scc_add_to_rx_queue(d, idata, d->scc_nr
410 * 2 + port);
411
412 /* TX interrupt: */
413 if (d->scc_register_w[port * N_SCC_REGS + SCC_WR9] &
414 SCC_WR9_MASTER_IE &&
415 d->scc_register_w[port * N_SCC_REGS + SCC_WR1] &
416 SCC_WR1_TX_IE) {
417 if (port == SCC_CHANNEL_A)
418 d->scc_register_r[N_SCC_REGS + SCC_RR3]
419 |= SCC_RR3_TX_IP_A;
420 else
421 d->scc_register_r[N_SCC_REGS + SCC_RR3]
422 |= SCC_RR3_TX_IP_B;
423 }
424
425 dev_scc_tick(cpu, extra);
426 }
427 break;
428 default:
429 if (writeflag==MEM_READ) {
430 debug("[ scc: (port %i) read from 0x%08lx ]\n",
431 port, (long)relative_addr);
432 } else {
433 debug("[ scc: (port %i) write to 0x%08lx: 0x%08x ]\n",
434 port, (long)relative_addr, (int)idata);
435 }
436 }
437
438 if (ultrix_mode && writeflag == MEM_READ) {
439 odata <<= 8;
440 }
441
442 if (writeflag == MEM_READ)
443 memory_writemax64(cpu, data, len, odata);
444
445 return 1;
446 }
447
448
449 /*
450 * dev_scc_init():
451 *
452 * use_fb = non-zero when using graphical console + keyboard
453 * scc_nr = 0 or 1
454 * addmul = 1 in most cases, 8 on SGI?
455 */
456 void *dev_scc_init(struct machine *machine, struct memory *mem,
457 uint64_t baseaddr, int irq_nr, int use_fb, int scc_nr, int addrmul)
458 {
459 struct scc_data *d;
460
461 d = malloc(sizeof(struct scc_data));
462 if (d == NULL) {
463 fprintf(stderr, "out of memory\n");
464 exit(1);
465 }
466 memset(d, 0, sizeof(struct scc_data));
467 d->irq_nr = irq_nr;
468 d->scc_nr = scc_nr;
469 d->use_fb = use_fb;
470 d->addrmul = addrmul;
471 d->console_handle = console_start_slave(machine, "SCC");
472
473 lk201_init(&d->lk201, use_fb, dev_scc_add_to_rx_queue,
474 d->console_handle, d);
475
476 memory_device_register(mem, "scc", baseaddr, DEV_SCC_LENGTH,
477 dev_scc_access, d, MEM_DEFAULT, NULL);
478 machine_add_tickfunction(machine, dev_scc_tick, d, SCC_TICK_SHIFT);
479
480 return (void *) d;
481 }
482

  ViewVC Help
Powered by ViewVC 1.1.26