/[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 30 - (hide annotations)
Mon Oct 8 16:20:40 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 13317 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1325 2006/08/15 15:38:37 debug Exp $
20060723	More Transputer instructions (pfix, nfix, opr, mint, ldl, ldlp,
		eqc, rev, ajw, stl, stlf, sthf, sub, ldnl, ldnlp, ldpi, move,
		wcnt, add, bcnt).
		Adding more SPARC instructions (andcc, addcc, bl, rdpr).
		Progress on the igsfb framebuffer used by NetBSD/netwinder.
		Enabling 8-bit fills in dev_fb.
		NetBSD/netwinder 3.0.1 can now run from a disk image :-)
20060724	Cleanup/performance fix for 64-bit virtual translation table
		updates (by removing the "timestamp" stuff). A full NetBSD/pmax
		3.0.1 install for R4400 has dropped from 667 seconds to 584 :)
		Fixing the igsfb "almost vga" color (it is 24-bit, not 18-bit).
		Adding some MIPS instruction combinations (3*lw, and 3*addu).
		The 8048 keyboard now turns off interrupt enable between the
		KBR_ACK and the KBR_RSTDONE, to work better with Linux 2.6.
		Not causing PPC DEC interrupts if PPC_NO_DEC is set for a
		specific CPU; NetBSD/bebox gets slightly further than before.
		Adding some more SPARC instructions: branches, udiv.
20060725	Refreshing dev_pckbc.c a little.
		Cleanups for the SH emulation mode, and adding the first
		"compact" (16-bit) instructions: various simple movs, nop,
		shll, stc, or, ldc.
20060726	Adding dummy "pcn" (AMD PCnet NIC) PCI glue.
20060727	Various cleanups; removing stuff from cpu.h, such as
		running_translated (not really meaningful anymore), and
		page flags (breaking into the debugger clears all translations
		anyway).
		Minor MIPS instruction combination updates.
20060807	Expanding the 3*sw and 3*lw MIPS instruction combinations to
		work with 2* and 4* too, resulting in a minor performance gain.
		Implementing a usleep hack for the RM52xx/MIPS32/MIPS64 "wait"
		instruction (when emulating 1 cpu).
20060808	Experimenting with some more MIPS instruction combinations.
		Implementing support for showing a (hardcoded 12x22) text
		cursor in igsfb.
20060809	Simplifying the NetBSD/evbmips (Malta) install instructions
		somewhat (by using a NetBSD/pmax ramdisk install kernel).
20060812	Experimenting more with the MIPS 'wait' instruction.
		PCI configuration register writes can now be handled, which
		allow PCI IDE controllers to work with NetBSD/Malta 3.0.1 and
		NetBSD/cobalt 3.0.1. (Previously only NetBSD 2.1 worked.)
20060813	Updating dev_gt.c based on numbers from Alec Voropay, to enable
		Linux 2.6 to use PCI on Malta.
		Continuing on Algor interrupt stuff.
20060814	Adding support for routing ISA interrupts to two different
		interrupts, making it possible to run NetBSD/algor :-)
20060814-15	Testing for the release.

==============  RELEASE 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