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

Annotation of /trunk/src/devices/dev_scc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (hide 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 dpavlin 4 /*
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