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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Mon Oct 8 16:17:52 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 13182 byte(s)
0.3.1
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