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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26