/[gxemul]/trunk/src/devices/dev_mc146818.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_mc146818.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 34 - (hide annotations)
Mon Oct 8 16:21:17 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 19059 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1480 2007/02/19 01:34:42 debug Exp $
20061029	Changing usleep(1) calls in the debugger to usleep(10000)
20061107	Adding a new disk image option (-d o...) which sets the ISO9660
		filesystem base offset; also making some other hacks to allow
		NetBSD/dreamcast and homebrew demos/games to boot directly
		from a filesystem image.
		Moving Dreamcast-specific stuff in the documentation to its
		own page (dreamcast.html).
		Adding a border to the Dreamcast PVR framebuffer.
20061108	Adding a -T command line option (again?), for halting the
		emulator on unimplemented memory accesses.
20061109	Continuing on various SH4 and Dreamcast related things.
		The emulator should now halt on more unimplemented device
		accesses, instead of just printing a warning, forcing me to
		actually implement missing stuff :)
20061111	Continuing on SH4 and Dreamcast stuff.
		Adding a bogus Landisk (SH4) machine mode.
20061112	Implementing some parts of the Dreamcast GDROM device. With
		some ugly hacks, NetBSD can (barely) mount an ISO image.
20061113	NetBSD/dreamcast now starts booting from the Live CD image,
		but crashes randomly quite early on in the boot process.
20061122	Beginning on a skeleton interrupt.h and interrupt.c for the
		new interrupt subsystem.
20061124	Continuing on the new interrupt system; taking the first steps
		to attempt to connect CPUs (SuperH and MIPS) and devices
		(dev_cons and SH4 timer interrupts) to it. Many things will
		probably break from now on.
20061125	Converting dev_ns16550, dev_8253 to the new interrupt system.
		Attempting to begin to convert the ISA bus.
20061130	Incorporating a patch from Brian Foley for the configure
		script, which checks for X11 libs in /usr/X11R6/lib64 (which
		is used on some Linux systems).
20061227	Adding a note in the man page about booting from Dreamcast
		CDROM images (i.e. that no external kernel is needed).
20061229	Continuing on the interrupt system rewrite: beginning to
		convert more devices, adding abort() calls for legacy interrupt
		system calls so that everything now _has_ to be rewritten!
		Almost all machine modes are now completely broken.
20061230	More progress on removing old interrupt code, mostly related
		to the ISA bus + devices, the LCA bus (on AlphaBook1), and
		the Footbridge bus (for CATS). And some minor PCI stuff.
		Connecting the ARM cpu to the new interrupt system.
		The CATS, NetWinder, and QEMU_MIPS machine modes now work with
		the new interrupt system :)
20061231	Connecting PowerPC CPUs to the new interrupt system.
		Making PReP machines (IBM 6050) work again.
		Beginning to convert the GT PCI controller (for e.g. Malta
		and Cobalt emulation). Some things work, but not everything.
		Updating Copyright notices for 2007.
20070101	Converting dev_kn02 from legacy style to devinit; the 3max
		machine mode now works with the new interrupt system :-]
20070105	Beginning to convert the SGI O2 machine to the new interrupt
		system; finally converting O2 (IP32) devices to devinit, etc.
20070106	Continuing on the interrupt system redesign/rewrite; KN01
		(PMAX), KN230, and Dreamcast ASIC interrupts should work again,
		moving out stuff from machine.h and devices.h into the
		corresponding devices, beginning the rewrite of i80321
		interrupts, etc.
20070107	Beginning on the rewrite of Eagle interrupt stuff (PReP, etc).
20070117	Beginning the rewrite of Algor (V3) interrupts (finally
		changing dev_v3 into devinit style).
20070118	Removing the "bus" registry concept from machine.h, because
		it was practically meaningless.
		Continuing on the rewrite of Algor V3 ISA interrupts.
20070121	More work on Algor interrupts; they are now working again,
		well enough to run NetBSD/algor. :-)
20070122	Converting VR41xx (HPCmips) interrupts. NetBSD/hpcmips
		can be installed using the new interrupt system :-)
20070123	Making the testmips mode work with the new interrupt system.
20070127	Beginning to convert DEC5800 devices to devinit, and to the
		new interrupt system.
		Converting Playstation 2 devices to devinit, and converting
		the interrupt system. Also fixing a severe bug: the interrupt
		mask register on Playstation 2 is bitwise _toggled_ on writes.
20070128	Removing the dummy NetGear machine mode and the 8250 device
		(which was only used by the NetGear machine).
		Beginning to convert the MacPPC GC (Grand Central) interrupt
		controller to the new interrupt system.
		Converting Jazz interrupts (PICA61 etc.) to the new interrupt
		system. NetBSD/arc can be installed again :-)
		Fixing the JAZZ timer (hardcoding it at 100 Hz, works with
		NetBSD and it is better than a completely dummy timer as it
		was before).
		Converting dev_mp to the new interrupt system, although I
		haven't had time to actually test it yet.
		Completely removing src/machines/interrupts.c, cpu_interrupt
		and cpu_interrupt_ack in src/cpu.c, and
		src/include/machine_interrupts.h! Adding fatal error messages
		+ abort() in the few places that are left to fix.
		Converting dev_z8530 to the new interrupt system.
		FINALLY removing the md_int struct completely from the
		machine struct.
		SH4 fixes (adding a PADDR invalidation in the ITLB replacement
		code in memory_sh.c); the NetBSD/dreamcast LiveCD now runs
		all the way to the login prompt, and can be interacted with :-)
		Converting the CPC700 controller (PCI and interrupt controller
		for PM/PPC) to the new interrupt system.
20070129	Fixing MACE ISA interrupts (SGI IP32 emulation). Both NetBSD/
		sgimips' and OpenBSD/sgi's ramdisk kernels can now be
		interacted with again.
20070130	Moving out the MIPS multi_lw and _sw instruction combinations
		so that they are auto-generated at compile time instead.
20070131	Adding detection of amd64/x86_64 hosts in the configure script,
		for doing initial experiments (again :-) with native code
		generation.
		Adding a -k command line option to set the size of the dyntrans
		cache, and a -B command line option to disable native code
		generation, even if GXemul was compiled with support for
		native code generation for the specific host CPU architecture.
20070201	Experimenting with a skeleton for native code generation.
		Changing the default behaviour, so that native code generation
		is now disabled by default, and has to be enabled by using
		-b on the command line.
20070202	Continuing the native code generation experiments.
		Making PCI interrupts work for Footbridge again.
20070203	More native code generation experiments.
		Removing most of the native code generation experimental code,
		it does not make sense to include any quick hacks like this.
		Minor cleanup/removal of some more legacy MIPS interrupt code.
20070204	Making i80321 interrupts work again (for NetBSD/evbarm etc.),
		and fixing the timer at 100 Hz.
20070206	Experimenting with removing the wdc interrupt slowness hack.
20070207	Lowering the number of dyntrans TLB entries for MIPS from
		192 to 128, resulting in a minor speed improvement.
		Minor optimization to the code invalidation routine in
		cpu_dyntrans.c.
20070208	Increasing (experimentally) the nr of dyntrans instructions per
		loop from 60 to 120.
20070210	Commenting out (experimentally) the dyntrans_device_danger
		detection in memory_rw.c.
		Changing the testmips and baremips machines to use a revision 2
		MIPS64 CPU by default, instead of revision 1.
		Removing the dummy i960, IA64, x86, AVR32, and HP PA-RISC
		files, the PC bios emulation, and the Olivetti M700 (ARC) and
		db64360 emulation modes.
20070211	Adding an "mp" demo to the demos directory, which tests the
		SMP functionality of the testmips machine.
		Fixing PReP interrupts some more. NetBSD/prep now boots again.
20070216	Adding a "nop workaround" for booting Mach/PMAX to the
		documentation; thanks to Artur Bujdoso for the values.
		Converting more of the MacPPC interrupt stuff to the new
		system.
		Beginning to convert BeBox interrupts to the new system.
		PPC603e should NOT have the PPC_NO_DEC flag! Removing it.
		Correcting BeBox clock speed (it was set to 100 in the NetBSD
		bootinfo block, but should be 33000000/4), allowing NetBSD
		to start without using the (incorrect) PPC_NO_DEC hack.
20070217	Implementing (slow) AltiVec vector loads and stores, allowing
		NetBSD/macppc to finally boot using the GENERIC kernel :-)
		Updating the documentation with install instructions for
		NetBSD/macppc.
20070218-19	Regression testing for the release.

==============  RELEASE 0.4.4  ==============


1 dpavlin 4 /*
2 dpavlin 34 * Copyright (C) 2003-2007 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 34 * $Id: dev_mc146818.c,v 1.95 2007/02/03 16:55:55 debug Exp $
29 dpavlin 4 *
30     * MC146818 real-time clock, used by many different machines types.
31 dpavlin 16 * (DS1687 as used in some other machines is also similar to the MC146818.)
32 dpavlin 4 *
33     * This device contains Date/time, the machine's ethernet address (on
34     * DECstation 3100), and can cause periodic (hardware) interrupts.
35     *
36     * NOTE: Many register offsets are multiplied by 4 in this code; this is
37     * because I originally wrote it for DECstation 3100 emulation, where the
38     * registered are spaced that way.
39     */
40    
41     #include <stdio.h>
42     #include <stdlib.h>
43     #include <string.h>
44     #include <time.h>
45    
46     #include "cpu.h"
47     #include "devices.h"
48     #include "machine.h"
49     #include "memory.h"
50     #include "misc.h"
51 dpavlin 32 #include "timer.h"
52 dpavlin 4
53     #include "mc146818reg.h"
54    
55    
56     #define to_bcd(x) ( ((x)/10) * 16 + ((x)%10) )
57 dpavlin 20 #define from_bcd(x) ( ((x)>>4) * 10 + ((x)&15) )
58 dpavlin 4
59     /* #define MC146818_DEBUG */
60    
61 dpavlin 12 #define TICK_SHIFT 14
62 dpavlin 4
63    
64     /* 256 on DECstation, SGI uses reg at 72*4 as the Century */
65     #define N_REGISTERS 1024
66     struct mc_data {
67 dpavlin 32 int access_style;
68     int last_addr;
69 dpavlin 4
70 dpavlin 32 int register_choice;
71     int reg[N_REGISTERS];
72     int addrdiv;
73 dpavlin 4
74 dpavlin 32 int use_bcd;
75 dpavlin 4
76 dpavlin 32 int timebase_hz;
77     int interrupt_hz;
78     int old_interrupt_hz;
79 dpavlin 34 struct interrupt irq;
80 dpavlin 32 struct timer *timer;
81     volatile int pending_timer_interrupts;
82 dpavlin 4
83 dpavlin 32 int previous_second;
84     int n_seconds_elapsed;
85     int uip_threshold;
86 dpavlin 4
87 dpavlin 32 int ugly_netbsd_prep_hack_done;
88     int ugly_netbsd_prep_hack_sec;
89 dpavlin 4 };
90    
91    
92     /*
93 dpavlin 20 * Ugly hack to fool NetBSD/prep to accept the clock. (See mcclock_isa_match
94     * in NetBSD's arch/prep/isa/mcclock_isa.c for details.)
95     */
96     #define NETBSD_HACK_INIT 0
97     #define NETBSD_HACK_FIRST_1 1
98     #define NETBSD_HACK_FIRST_2 2
99     #define NETBSD_HACK_SECOND_1 3
100     #define NETBSD_HACK_SECOND_2 4
101     #define NETBSD_HACK_DONE 5
102    
103    
104     /*
105 dpavlin 32 * timer_tick():
106 dpavlin 4 *
107 dpavlin 32 * Called d->interrupt_hz times per (real-world) second.
108 dpavlin 4 */
109 dpavlin 32 static void timer_tick(struct timer *timer, void *extra)
110 dpavlin 4 {
111 dpavlin 32 struct mc_data *d = (struct mc_data *) extra;
112     d->pending_timer_interrupts ++;
113 dpavlin 4 }
114    
115    
116 dpavlin 32 DEVICE_TICK(mc146818)
117 dpavlin 4 {
118     struct mc_data *d = extra;
119 dpavlin 32 int pti = d->pending_timer_interrupts;
120 dpavlin 4
121 dpavlin 32 if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && pti > 0) {
122     static int warned = 0;
123 dpavlin 34 if (pti > 1500 && !warned) {
124 dpavlin 32 warned = 1;
125     fatal("[ WARNING: MC146818 interrupts lost, "
126     "host too slow? ]\n");
127     }
128 dpavlin 4
129 dpavlin 32 #if 0
130     /* For debugging, to see how much the interrupts are
131     lagging behind the real clock: */
132     {
133     static int x = 0;
134     if (++x == 1) {
135     x = 0;
136     printf("%i ", pti);
137     fflush(stdout);
138     }
139     }
140     #endif
141 dpavlin 4
142 dpavlin 34 INTERRUPT_ASSERT(d->irq);
143 dpavlin 4
144 dpavlin 32 d->reg[MC_REGC * 4] |= MC_REGC_PF;
145 dpavlin 4 }
146    
147     if (d->reg[MC_REGC * 4] & MC_REGC_UF ||
148     d->reg[MC_REGC * 4] & MC_REGC_AF ||
149     d->reg[MC_REGC * 4] & MC_REGC_PF)
150     d->reg[MC_REGC * 4] |= MC_REGC_IRQF;
151     }
152    
153    
154     /*
155     * dev_mc146818_jazz_access():
156     *
157     * It seems like JAZZ machines accesses the mc146818 by writing one byte to
158     * 0x90000070 and then reading or writing another byte at 0x......0004000.
159     */
160     int dev_mc146818_jazz_access(struct cpu *cpu, struct memory *mem,
161     uint64_t relative_addr, unsigned char *data, size_t len,
162     int writeflag, void *extra)
163     {
164     struct mc_data *d = extra;
165    
166     #ifdef MC146818_DEBUG
167     if (writeflag == MEM_WRITE) {
168     int i;
169     fatal("[ mc146818_jazz: write to addr=0x%04x: ",
170     (int)relative_addr);
171     for (i=0; i<len; i++)
172     fatal("%02x ", data[i]);
173     fatal("]\n");
174     } else
175     fatal("[ mc146818_jazz: read from addr=0x%04x ]\n",
176     (int)relative_addr);
177     #endif
178    
179     if (writeflag == MEM_WRITE) {
180     d->last_addr = data[0];
181     return 1;
182     } else {
183     data[0] = d->last_addr;
184     return 1;
185     }
186     }
187    
188    
189     /*
190     * mc146818_update_time():
191     *
192     * This function updates the MC146818 registers by reading
193     * the host's clock.
194     */
195     static void mc146818_update_time(struct mc_data *d)
196     {
197     struct tm *tmp;
198     time_t timet;
199    
200     timet = time(NULL);
201     tmp = gmtime(&timet);
202    
203     d->reg[4 * MC_SEC] = tmp->tm_sec;
204     d->reg[4 * MC_MIN] = tmp->tm_min;
205     d->reg[4 * MC_HOUR] = tmp->tm_hour;
206     d->reg[4 * MC_DOW] = tmp->tm_wday + 1;
207     d->reg[4 * MC_DOM] = tmp->tm_mday;
208     d->reg[4 * MC_MONTH] = tmp->tm_mon + 1;
209     d->reg[4 * MC_YEAR] = tmp->tm_year;
210    
211 dpavlin 16 /*
212     * Special hacks for emulating the behaviour of various machines:
213     */
214 dpavlin 4 switch (d->access_style) {
215 dpavlin 20 case MC146818_ALGOR:
216 dpavlin 32 /*
217     * NetBSD/evbmips sources indicate that the Algor year base
218     * is 1920. This makes the time work with NetBSD in Malta
219     * emulation. However, for Linux, commenting out this line
220     * works better. (TODO: Find a way to make both work?)
221     */
222 dpavlin 20 d->reg[4 * MC_YEAR] += 80;
223     break;
224 dpavlin 4 case MC146818_ARC_NEC:
225     d->reg[4 * MC_YEAR] += (0x18 - 104);
226     break;
227 dpavlin 16 case MC146818_CATS:
228     d->reg[4 * MC_YEAR] %= 100;
229     break;
230 dpavlin 4 case MC146818_SGI:
231     /*
232     * NetBSD/sgimips assumes data in BCD format.
233     * Also, IRIX stores the year value in a weird
234     * format, according to ../arch/sgimips/sgimips/clockvar.h
235     * in NetBSD:
236     *
237     * "If year < 1985, store (year - 1970), else
238     * (year - 1940). This matches IRIX semantics."
239     *
240     * Another rule: It seems that a real SGI IP32 box
241     * uses the value 5 for the year 2005.
242     */
243     d->reg[4 * MC_YEAR] =
244     d->reg[4 * MC_YEAR] >= 100 ?
245     (d->reg[4 * MC_YEAR] - 100) :
246     (
247     d->reg[4 * MC_YEAR] < 85 ?
248     (d->reg[4 * MC_YEAR] - 30 + 40)
249     : (d->reg[4 * MC_YEAR] - 40)
250     );
251     /* Century: */
252     d->reg[72 * 4] = 19 + (tmp->tm_year / 100);
253     break;
254     case MC146818_DEC:
255     /*
256     * DECstations must have 72 or 73 in the
257     * Year field, or Ultrix screems. (Weird.)
258     */
259     d->reg[4 * MC_YEAR] = 72;
260    
261     /*
262     * Linux on DECstation stores the year in register 63,
263     * but no other DECstation OS does? (Hm.)
264     */
265     d->reg[4 * 63] = tmp->tm_year - 100;
266     break;
267     }
268    
269     if (d->use_bcd) {
270     d->reg[4 * MC_SEC] = to_bcd(d->reg[4 * MC_SEC]);
271     d->reg[4 * MC_MIN] = to_bcd(d->reg[4 * MC_MIN]);
272     d->reg[4 * MC_HOUR] = to_bcd(d->reg[4 * MC_HOUR]);
273     d->reg[4 * MC_DOW] = to_bcd(d->reg[4 * MC_DOW]);
274     d->reg[4 * MC_DOM] = to_bcd(d->reg[4 * MC_DOM]);
275     d->reg[4 * MC_MONTH] = to_bcd(d->reg[4 * MC_MONTH]);
276     d->reg[4 * MC_YEAR] = to_bcd(d->reg[4 * MC_YEAR]);
277    
278     /* Used by Linux on DECstation: (Hm) */
279     d->reg[4 * 63] = to_bcd(d->reg[4 * 63]);
280    
281     /* Used on SGI: */
282     d->reg[4 * 72] = to_bcd(d->reg[4 * 72]);
283     }
284     }
285    
286    
287     /*
288     * dev_mc146818_access():
289     *
290     * TODO: This access function only handles 8-bit accesses!
291     */
292     int dev_mc146818_access(struct cpu *cpu, struct memory *mem,
293     uint64_t r, unsigned char *data, size_t len,
294     int writeflag, void *extra)
295     {
296     struct tm *tmp;
297     time_t timet;
298     struct mc_data *d = extra;
299 dpavlin 22 int relative_addr = r;
300     size_t i;
301 dpavlin 4
302     relative_addr /= d->addrdiv;
303    
304     /* Different ways of accessing the registers: */
305     switch (d->access_style) {
306 dpavlin 20 case MC146818_ALGOR:
307 dpavlin 16 case MC146818_CATS:
308 dpavlin 4 case MC146818_PC_CMOS:
309 dpavlin 16 if ((relative_addr & 1) == 0x00) {
310 dpavlin 4 if (writeflag == MEM_WRITE) {
311     d->last_addr = data[0];
312     return 1;
313     } else {
314     data[0] = d->last_addr;
315     return 1;
316     }
317 dpavlin 16 } else
318 dpavlin 4 relative_addr = d->last_addr * 4;
319     break;
320     case MC146818_ARC_NEC:
321     if (relative_addr == 0x01) {
322     if (writeflag == MEM_WRITE) {
323     d->last_addr = data[0];
324     return 1;
325     } else {
326     data[0] = d->last_addr;
327     return 1;
328     }
329     } else if (relative_addr == 0x00)
330     relative_addr = d->last_addr * 4;
331     else {
332     fatal("[ mc146818: not accessed as an "
333     "MC146818_ARC_NEC device! ]\n");
334     }
335     break;
336     case MC146818_ARC_JAZZ:
337     /* See comment for dev_mc146818_jazz_access(). */
338     relative_addr = d->last_addr * 4;
339     break;
340     case MC146818_DEC:
341     case MC146818_SGI:
342     /*
343     * This device was originally written for DECstation
344     * emulation, so no changes are necessary for that access
345     * style.
346     *
347     * SGI access bytes 0x0..0xd at offsets 0x0yz..0xdyz, where yz
348     * should be ignored. It works _almost_ as DEC, if offsets are
349     * divided by 0x40.
350     */
351 dpavlin 20 break;
352     case MC146818_PMPPC:
353     relative_addr *= 4;
354     break;
355 dpavlin 4 default:
356     ;
357     }
358    
359     #ifdef MC146818_DEBUG
360     if (writeflag == MEM_WRITE) {
361     fatal("[ mc146818: write to addr=0x%04x (len %i): ",
362     (int)relative_addr, (int)len);
363     for (i=0; i<len; i++)
364 dpavlin 20 fatal("0x%02x ", data[i]);
365 dpavlin 4 fatal("]\n");
366     }
367     #endif
368    
369     /*
370 dpavlin 12 * Sprite seems to wants UF interrupt status, once every second, or
371 dpavlin 4 * it hangs forever during bootup. (These do not cause interrupts,
372     * but it is good enough... Sprite polls this, iirc.)
373 dpavlin 12 *
374     * Linux on at least sgimips and evbmips (Malta) wants the UIP bit
375     * in REGA to be updated once a second.
376 dpavlin 4 */
377 dpavlin 20 if (relative_addr == MC_REGA*4 || relative_addr == MC_REGC*4) {
378     timet = time(NULL);
379     tmp = gmtime(&timet);
380     d->reg[MC_REGC * 4] &= ~MC_REGC_UF;
381     if (tmp->tm_sec != d->previous_second) {
382     d->n_seconds_elapsed ++;
383     d->previous_second = tmp->tm_sec;
384     }
385     if (d->n_seconds_elapsed > d->uip_threshold) {
386     d->n_seconds_elapsed = 0;
387 dpavlin 12
388 dpavlin 20 d->reg[MC_REGA * 4] |= MC_REGA_UIP;
389 dpavlin 12
390 dpavlin 20 d->reg[MC_REGC * 4] |= MC_REGC_UF;
391     d->reg[MC_REGC * 4] |= MC_REGC_IRQF;
392 dpavlin 4
393 dpavlin 20 /* For some reason, some Linux/DECstation KN04
394     kernels want the PF (periodic flag) bit set,
395     even though interrupts are not enabled? */
396     d->reg[MC_REGC * 4] |= MC_REGC_PF;
397     } else
398     d->reg[MC_REGA * 4] &= ~MC_REGA_UIP;
399     }
400 dpavlin 4
401     /* RTC data is in either BCD format or binary: */
402 dpavlin 12 if (d->use_bcd)
403 dpavlin 4 d->reg[MC_REGB * 4] &= ~(1 << 2);
404 dpavlin 12 else
405 dpavlin 4 d->reg[MC_REGB * 4] |= (1 << 2);
406    
407     /* RTC date/time is always Valid: */
408     d->reg[MC_REGD * 4] |= MC_REGD_VRT;
409    
410     if (writeflag == MEM_WRITE) {
411     /* WRITE: */
412     switch (relative_addr) {
413     case MC_REGA*4:
414     if ((data[0] & MC_REGA_DVMASK) == MC_BASE_32_KHz)
415     d->timebase_hz = 32000;
416     if ((data[0] & MC_REGA_DVMASK) == MC_BASE_1_MHz)
417     d->timebase_hz = 1000000;
418     if ((data[0] & MC_REGA_DVMASK) == MC_BASE_4_MHz)
419     d->timebase_hz = 4000000;
420     switch (data[0] & MC_REGA_RSMASK) {
421     case MC_RATE_NONE:
422     d->interrupt_hz = 0;
423     break;
424     case MC_RATE_1:
425     if (d->timebase_hz == 32000)
426     d->interrupt_hz = 256;
427     else
428     d->interrupt_hz = 32768;
429     break;
430     case MC_RATE_2:
431     if (d->timebase_hz == 32000)
432     d->interrupt_hz = 128;
433     else
434     d->interrupt_hz = 16384;
435     break;
436     case MC_RATE_8192_Hz: d->interrupt_hz = 8192; break;
437     case MC_RATE_4096_Hz: d->interrupt_hz = 4096; break;
438     case MC_RATE_2048_Hz: d->interrupt_hz = 2048; break;
439     case MC_RATE_1024_Hz: d->interrupt_hz = 1024; break;
440     case MC_RATE_512_Hz: d->interrupt_hz = 512; break;
441     case MC_RATE_256_Hz: d->interrupt_hz = 256; break;
442     case MC_RATE_128_Hz: d->interrupt_hz = 128; break;
443     case MC_RATE_64_Hz: d->interrupt_hz = 64; break;
444     case MC_RATE_32_Hz: d->interrupt_hz = 32; break;
445     case MC_RATE_16_Hz: d->interrupt_hz = 16; break;
446     case MC_RATE_8_Hz: d->interrupt_hz = 8; break;
447     case MC_RATE_4_Hz: d->interrupt_hz = 4; break;
448     case MC_RATE_2_Hz: d->interrupt_hz = 2; break;
449 dpavlin 20 default:/* debug("[ mc146818: unimplemented "
450 dpavlin 4 "MC_REGA RS: %i ]\n",
451     data[0] & MC_REGA_RSMASK); */
452     ;
453     }
454    
455 dpavlin 32 if (d->interrupt_hz != d->old_interrupt_hz) {
456     debug("[ rtc changed to interrupt at %i Hz ]\n",
457     d->interrupt_hz);
458 dpavlin 4
459 dpavlin 32 d->old_interrupt_hz = d->interrupt_hz;
460 dpavlin 4
461 dpavlin 32 if (d->timer == NULL)
462     d->timer = timer_add(d->interrupt_hz,
463     timer_tick, d);
464     else
465     timer_update_frequency(d->timer,
466     d->interrupt_hz);
467     }
468    
469 dpavlin 4 d->reg[MC_REGA * 4] =
470     data[0] & (MC_REGA_RSMASK | MC_REGA_DVMASK);
471     break;
472     case MC_REGB*4:
473     d->reg[MC_REGB*4] = data[0];
474     if (!(data[0] & MC_REGB_PIE)) {
475 dpavlin 34 INTERRUPT_DEASSERT(d->irq);
476 dpavlin 4 }
477 dpavlin 32
478 dpavlin 4 /* debug("[ mc146818: write to MC_REGB, data[0] "
479     "= 0x%02x ]\n", data[0]); */
480     break;
481     case MC_REGC*4:
482     d->reg[MC_REGC * 4] = data[0];
483     debug("[ mc146818: write to MC_REGC, data[0] = "
484     "0x%02x ]\n", data[0]);
485     break;
486     case 0x128:
487     d->reg[relative_addr] = data[0];
488     if (data[0] & 8) {
489 dpavlin 22 int j;
490    
491 dpavlin 4 /* Used on SGI to power off the machine. */
492     fatal("[ md146818: power off ]\n");
493 dpavlin 22 for (j=0; j<cpu->machine->ncpus; j++)
494     cpu->machine->cpus[j]->running = 0;
495 dpavlin 4 cpu->machine->
496     exit_without_entering_debugger = 1;
497     }
498     break;
499     default:
500     d->reg[relative_addr] = data[0];
501    
502     debug("[ mc146818: unimplemented write to "
503     "relative_addr = %08lx: ", (long)relative_addr);
504     for (i=0; i<len; i++)
505     debug("%02x ", data[i]);
506     debug("]\n");
507     }
508     } else {
509     /* READ: */
510     switch (relative_addr) {
511     case 0x01: /* Station's ethernet address (6 bytes) */
512     case 0x05: /* (on DECstation 3100) */
513     case 0x09:
514     case 0x0d:
515     case 0x11:
516     case 0x15:
517     break;
518     case 4 * MC_SEC:
519 dpavlin 20 if (d->ugly_netbsd_prep_hack_done < NETBSD_HACK_DONE) {
520     d->ugly_netbsd_prep_hack_done ++;
521     switch (d->ugly_netbsd_prep_hack_done) {
522     case NETBSD_HACK_FIRST_1:
523     d->ugly_netbsd_prep_hack_sec =
524     from_bcd(d->reg[relative_addr]);
525     break;
526     case NETBSD_HACK_FIRST_2:
527     d->reg[relative_addr] = to_bcd(
528     d->ugly_netbsd_prep_hack_sec);
529     break;
530     case NETBSD_HACK_SECOND_1:
531     case NETBSD_HACK_SECOND_2:
532     d->reg[relative_addr] = to_bcd((1 +
533     d->ugly_netbsd_prep_hack_sec) % 60);
534     break;
535     }
536     }
537 dpavlin 4 case 4 * MC_MIN:
538     case 4 * MC_HOUR:
539     case 4 * MC_DOW:
540     case 4 * MC_DOM:
541     case 4 * MC_MONTH:
542     case 4 * MC_YEAR:
543     case 4 * 63: /* 63 is used by Linux on DECstation */
544     case 4 * 72: /* 72 is Century, on SGI (DS1687) */
545     /*
546     * If the SET bit is set, then we don't automatically
547     * update the values. Otherwise, we update them by
548     * reading from the host's clock:
549     */
550     if (d->reg[MC_REGB * 4] & MC_REGB_SET)
551     break;
552    
553 dpavlin 20 if (d->ugly_netbsd_prep_hack_done >= NETBSD_HACK_DONE)
554     mc146818_update_time(d);
555 dpavlin 4 break;
556 dpavlin 20 case 4 * MC_REGA:
557     break;
558 dpavlin 4 case 4 * MC_REGC: /* Interrupt ack. */
559     /* NOTE: Acking is done below, _after_ the
560     register has been read. */
561     break;
562 dpavlin 20 default:debug("[ mc146818: read from relative_addr = "
563 dpavlin 4 "%04x ]\n", (int)relative_addr);
564     }
565    
566     data[0] = d->reg[relative_addr];
567    
568     if (relative_addr == MC_REGC*4) {
569 dpavlin 34 INTERRUPT_DEASSERT(d->irq);
570 dpavlin 32
571     /*
572     * Acknowledging an interrupt decreases the
573     * number of pending "real world" timer ticks.
574     */
575 dpavlin 34 if (d->reg[MC_REGC * 4] & MC_REGC_PF &&
576     d->pending_timer_interrupts > 0)
577 dpavlin 32 d->pending_timer_interrupts --;
578    
579 dpavlin 4 d->reg[MC_REGC * 4] = 0x00;
580     }
581     }
582    
583     #ifdef MC146818_DEBUG
584     if (writeflag == MEM_READ) {
585     fatal("[ mc146818: read from addr=0x%04x (len %i): ",
586     (int)relative_addr, (int)len);
587     for (i=0; i<len; i++)
588 dpavlin 20 fatal("0x%02x ", data[i]);
589 dpavlin 4 fatal("]\n");
590     }
591     #endif
592    
593     return 1;
594     }
595    
596    
597     /*
598     * dev_mc146818_init():
599     *
600     * This needs to work for both DECstation emulation and other machine types,
601     * so it contains both rtc related stuff and the station's Ethernet address.
602     */
603     void dev_mc146818_init(struct machine *machine, struct memory *mem,
604 dpavlin 34 uint64_t baseaddr, char *irq_path, int access_style, int addrdiv)
605 dpavlin 4 {
606     unsigned char ether_address[6];
607     int i, dev_len;
608     struct mc_data *d;
609    
610     d = malloc(sizeof(struct mc_data));
611     if (d == NULL) {
612     fprintf(stderr, "out of memory\n");
613     exit(1);
614     }
615    
616     memset(d, 0, sizeof(struct mc_data));
617 dpavlin 34
618 dpavlin 4 d->access_style = access_style;
619     d->addrdiv = addrdiv;
620    
621 dpavlin 34 INTERRUPT_CONNECT(irq_path, d->irq);
622    
623 dpavlin 6 d->use_bcd = 0;
624 dpavlin 20 switch (access_style) {
625     case MC146818_SGI:
626     case MC146818_PC_CMOS:
627     case MC146818_PMPPC:
628 dpavlin 6 d->use_bcd = 1;
629 dpavlin 20 }
630 dpavlin 6
631 dpavlin 20 if (machine->machine_type != MACHINE_PREP) {
632     /* NetBSD/prep has a really ugly clock detection code;
633 dpavlin 22 no other machines/OSes don't need this. */
634 dpavlin 20 d->ugly_netbsd_prep_hack_done = NETBSD_HACK_DONE;
635     }
636    
637 dpavlin 6 if (access_style == MC146818_DEC) {
638 dpavlin 4 /* Station Ethernet Address, on DECstation 3100: */
639     for (i=0; i<6; i++)
640     ether_address[i] = 0x10 * (i+1);
641    
642 dpavlin 6 d->reg[0x01] = ether_address[0];
643     d->reg[0x05] = ether_address[1];
644     d->reg[0x09] = ether_address[2];
645     d->reg[0x0d] = ether_address[3];
646     d->reg[0x11] = ether_address[4];
647     d->reg[0x15] = ether_address[5];
648     /* TODO: 19, 1d, 21, 25 = checksum bytes 1,2,2,1 resp. */
649     d->reg[0x29] = ether_address[5];
650     d->reg[0x2d] = ether_address[4];
651     d->reg[0x31] = ether_address[3];
652     d->reg[0x35] = ether_address[2];
653     d->reg[0x39] = ether_address[1];
654     d->reg[0x3d] = ether_address[1];
655     d->reg[0x41] = ether_address[0];
656     d->reg[0x45] = ether_address[1];
657     d->reg[0x49] = ether_address[2];
658     d->reg[0x4d] = ether_address[3];
659     d->reg[0x51] = ether_address[4];
660     d->reg[0x55] = ether_address[5];
661     /* TODO: 59, 5d = checksum bytes 1,2 resp. */
662     d->reg[0x61] = 0xff;
663     d->reg[0x65] = 0x00;
664     d->reg[0x69] = 0x55;
665     d->reg[0x6d] = 0xaa;
666     d->reg[0x71] = 0xff;
667     d->reg[0x75] = 0x00;
668     d->reg[0x79] = 0x55;
669     d->reg[0x7d] = 0xaa;
670 dpavlin 4
671     /* Battery valid, for DECstations */
672     d->reg[0xf8] = 1;
673     }
674    
675 dpavlin 20 /*
676     * uip_threshold should ideally be 1, but when Linux polls the UIP bit
677     * it looses speed. This hack gives Linux the impression that the cpu
678     * is uip_threshold times faster than the slow clock it would
679     * otherwise detect.
680     *
681     * TODO: Find out if this messes up Sprite emulation; if so, then
682     * this hack has to be removed.
683     */
684 dpavlin 22 d->uip_threshold = 8;
685 dpavlin 20
686 dpavlin 4 if (access_style == MC146818_ARC_JAZZ)
687     memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,
688 dpavlin 20 1, dev_mc146818_jazz_access, d, DM_DEFAULT, NULL);
689 dpavlin 4
690     dev_len = DEV_MC146818_LENGTH;
691     switch (access_style) {
692 dpavlin 16 case MC146818_CATS:
693 dpavlin 4 case MC146818_PC_CMOS:
694     dev_len = 2;
695     break;
696     case MC146818_SGI:
697     dev_len = 0x400;
698     }
699    
700     memory_device_register(mem, "mc146818", baseaddr,
701     dev_len * addrdiv, dev_mc146818_access,
702 dpavlin 20 d, DM_DEFAULT, NULL);
703 dpavlin 4
704     mc146818_update_time(d);
705    
706 dpavlin 24 machine_add_tickfunction(machine, dev_mc146818_tick, d,
707     TICK_SHIFT, 0.0);
708 dpavlin 4 }
709    

  ViewVC Help
Powered by ViewVC 1.1.26