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

Annotation of /upstream/0.3.1/devices/dev_scc.c

Parent Directory Parent Directory | Revision Log Revision Log


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