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

Annotation of /upstream/0.4.2/src/devices/dev_scc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (hide 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 dpavlin 4 /*
2 dpavlin 22 * Copyright (C) 2003-2006 Anders Gavare. All rights reserved.
3 dpavlin 4 *
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 30 * $Id: dev_scc.c,v 1.34 2006/07/23 14:37:34 debug Exp $
29 dpavlin 4 *
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 dpavlin 22 *
44     ******************************************************************************
45     * _____ ___ ____ ___ _
46     * |_ _/ _ \| _ \ / _ \| |
47     * | || | | | | | | | | | |
48     * | || |_| | |_| | |_| |_|
49     * |_| \___/|____/ \___/(_)
50     *
51     * Since this is actually a Z8530, it should be merged with dev_z8530.c!
52 dpavlin 4 */
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 dpavlin 30 DEVICE_TICK(scc)
151 dpavlin 4 {
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 dpavlin 22 DEVICE_ACCESS(scc)
299 dpavlin 4 {
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 dpavlin 18 if (writeflag == MEM_WRITE)
306     idata = memory_readmax64(cpu, data, len);
307 dpavlin 4
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 dpavlin 22 d->console_handle = console_start_slave(machine, "SCC", 1);
474 dpavlin 4
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 dpavlin 20 dev_scc_access, d, DM_DEFAULT, NULL);
480 dpavlin 24 machine_add_tickfunction(machine, dev_scc_tick, d, SCC_TICK_SHIFT, 0.0);
481 dpavlin 4
482     return (void *) d;
483     }
484    

  ViewVC Help
Powered by ViewVC 1.1.26