/[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 32 - (hide annotations)
Mon Oct 8 16:20:58 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 19010 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1421 2006/11/06 05:32:37 debug Exp $
20060816	Adding a framework for emulated/virtual timers (src/timer.c),
		using only setitimer().
		Rewriting the mc146818 to use the new timer framework.
20060817	Adding a call to gettimeofday() every now and then (once every
		second, at the moment) to resynch the timer if it drifts.
		Beginning to convert the ISA timer interrupt mechanism (8253
		and 8259) to use the new timer framework.
		Removing the -I command line option.
20060819	Adding the -I command line option again, with new semantics.
		Working on Footbridge timer interrupts; NetBSD/NetWinder and
		NetBSD/CATS now run at correct speed, but unfortunately with
		HUGE delays during bootup.
20060821	Some minor m68k updates. Adding the first instruction: nop. :)
		Minor Alpha emulation updates.
20060822	Adding a FreeBSD development specific YAMON environment
		variable ("khz") (as suggested by Bruce M. Simpson).
		Moving YAMON environment variable initialization from
		machine_evbmips.c into promemul/yamon.c, and adding some more
		variables.
		Continuing on the LCA PCI bus controller (for Alpha machines).
20060823	Continuing on the timer stuff: experimenting with MIPS count/
		compare interrupts connected to the timer framework.
20060825	Adding bogus SCSI commands 0x51 (SCSICDROM_READ_DISCINFO) and
		0x52 (SCSICDROM_READ_TRACKINFO) to the SCSI emulation layer,
		to allow NetBSD/pmax 4.0_BETA to be installed from CDROM.
		Minor updates to the LCA PCI controller.
20060827	Implementing a CHIP8 cpu mode, and a corresponding CHIP8
		machine, for fun. Disassembly support for all instructions,
		and most of the common instructions have been implemented: mvi,
		mov_imm, add_imm, jmp, rand, cls, sprite, skeq_imm, jsr,
		skne_imm, bcd, rts, ldr, str, mov, or, and, xor, add, sub,
		font, ssound, sdelay, gdelay, bogus skup/skpr, skeq, skne.
20060828	Beginning to convert the CHIP8 cpu in the CHIP8 machine to a
		(more correct) RCA 180x cpu. (Disassembly for all 1802
		instructions has been implemented, but no execution yet, and
		no 1805 extended instructions.)
20060829	Minor Alpha emulation updates.
20060830	Beginning to experiment a little with PCI IDE for SGI O2.
		Fixing the cursor key mappings for MobilePro 770 emulation.
		Fixing the LK201 warning caused by recent NetBSD/pmax.
		The MIPS R41xx standby, suspend, and hibernate instructions now
		behave like the RM52xx/MIPS32/MIPS64 wait instruction.
		Fixing dev_wdc so it calculates correct (64-bit) offsets before
		giving them to diskimage_access().
20060831	Continuing on Alpha emulation (OSF1 PALcode).
20060901	Minor Alpha updates; beginning on virtual memory pagetables.
		Removed the limit for max nr of devices (in preparation for
		allowing devices' base addresses to be changed during runtime).
		Adding a hack for MIPS [d]mfc0 select 0 (except the count
		register), so that the coproc register is simply copied.
		The MIPS suspend instruction now exits the emulator, instead
		of being treated as a wait instruction (this causes NetBSD/
		hpcmips to get correct 'halt' behavior).
		The VR41xx RTC now returns correct time.
		Connecting the VR41xx timer to the timer framework (fixed at
		128 Hz, for now).
		Continuing on SPARC emulation, adding more instructions:
		restore, ba_xcc, ble. The rectangle drawing demo works :)
		Removing the last traces of the old ENABLE_CACHE_EMULATION
		MIPS stuff (not usable with dyntrans anyway).
20060902	Splitting up src/net.c into several smaller files in its own
		subdirectory (src/net/).
20060903	Cleanup of the files in src/net/, to make them less ugly.
20060904	Continuing on the 'settings' subsystem.
		Minor progress on the SPARC emulation mode.
20060905	Cleanup of various things, and connecting the settings
		infrastructure to various subsystems (emul, machine, cpu, etc).
		Changing the lk201 mouse update routine to not rely on any
		emulated hardware framebuffer cursor coordinates, but instead
		always do (semi-usable) relative movements.
20060906	Continuing on the lk201 mouse stuff. Mouse behaviour with
		multiple framebuffers (which was working in Ultrix) is now
		semi-broken (but it still works, in a way).
		Moving the documentation about networking into its own file
		(networking.html), and refreshing it a bit. Adding an example
		of how to use ethernet frame direct-access (udp_snoop).
20060907	Continuing on the settings infrastructure.
20060908	Minor updates to SH emulation: for 32-bit emulation: delay
		slots and the 'jsr @Rn' instruction. I'm putting 64-bit SH5 on
		ice, for now.
20060909-10	Implementing some more 32-bit SH instructions. Removing the
		64-bit mode completely. Enough has now been implemented to run
		the rectangle drawing demo. :-)
20060912	Adding more SH instructions.
20060916	Continuing on SH emulation (some more instructions: div0u,
		div1, rotcl/rotcr, more mov instructions, dt, braf, sets, sett,
		tst_imm, dmuls.l, subc, ldc_rm_vbr, movt, clrt, clrs, clrmac).
		Continuing on the settings subsystem (beginning on reading/
		writing settings, removing bugs, and connecting more cpus to
		the framework).
20060919	More work on SH emulation; adding an ldc banked instruction,
		and attaching a 640x480 framebuffer to the Dreamcast machine
		mode (NetBSD/dreamcast prints the NetBSD copyright banner :-),
		and then panics).
20060920	Continuing on the settings subsystem.
20060921	Fixing the Footbridge timer stuff so that NetBSD/cats and
		NetBSD/netwinder boot up without the delays.
20060922	Temporarily hardcoding MIPS timer interrupt to 100 Hz. With
		'wait' support disabled, NetBSD/malta and Linux/malta run at
		correct speed.
20060923	Connecting dev_gt to the timer framework, so that NetBSD/cobalt
		runs at correct speed.
		Moving SH4-specific memory mapped registers into its own
		device (dev_sh4.c).
		Running with -N now prints "idling" instead of bogus nr of
		instrs/second (which isn't valid anyway) while idling.
20060924	Algor emulation should now run at correct speed.
		Adding disassembly support for some MIPS64 revision 2
		instructions: ext, dext, dextm, dextu.
20060926	The timer framework now works also when the MIPS wait
		instruction is used.
20060928	Re-implementing checks for coprocessor availability for MIPS
		cop0 instructions. (Thanks to Carl van Schaik for noticing the
		lack of cop0 availability checks.)
20060929	Implementing an instruction combination hack which treats
		NetBSD/pmax' idle loop as a wait-like instruction.
20060930	The ENTRYHI_R_MASK was missing in (at least) memory_mips_v2p.c,
		causing TLB lookups to sometimes succeed when they should have
		failed. (A big thank you to Juli Mallett for noticing the
		problem.)
		Adding disassembly support for more MIPS64 revision 2 opcodes
		(seb, seh, wsbh, jalr.hb, jr.hb, synci, ins, dins, dinsu,
		dinsm, dsbh, dshd, ror, dror, rorv, drorv, dror32). Also
		implementing seb, seh, dsbh, dshd, and wsbh.
		Implementing an instruction combination hack for Linux/pmax'
		idle loop, similar to the NetBSD/pmax case.
20061001	Changing the NetBSD/sgimips install instructions to extract
		files from an iso image, instead of downloading them via ftp.
20061002	More-than-31-bit userland addresses in memory_mips_v2p.c were
		not actually working; applying a fix from Carl van Schaik to
		enable them to work + making some other updates (adding kuseg
		support).
		Fixing hpcmips (vr41xx) timer initialization.
		Experimenting with O(n)->O(1) reduction in the MIPS TLB lookup
		loop. Seems to work both for R3000 and non-R3000.
20061003	Continuing a little on SH emulation (adding more control
		registers; mini-cleanup of memory_sh.c).
20061004	Beginning on a dev_rtc, a clock/timer device for the test
		machines; also adding a demo, and some documentation.
		Fixing a bug in SH "mov.w @(disp,pc),Rn" (the result wasn't
		sign-extended), and adding the addc and ldtlb instructions.
20061005	Contining on SH emulation: virtual to physical address
		translation, and a skeleton exception mechanism.
20061006	Adding more SH instructions (various loads and stores, rte,
		negc, muls.w, various privileged register-move instructions).
20061007	More SH instructions: various move instructions, trapa, div0s,
		float, fdiv, ftrc.
		Continuing on dev_rtc; removing the rtc demo.
20061008	Adding a dummy Dreamcast PROM module. (Homebrew Dreamcast
		programs using KOS libs need this.)
		Adding more SH instructions: "stc vbr,rn", rotl, rotr, fsca,
		fmul, fadd, various floating-point moves, etc. A 256-byte
		demo for Dreamcast runs :-)
20061012	Adding the SH "lds Rm,pr" and bsr instructions.
20061013	More SH instructions: "sts fpscr,rn", tas.b, and some more
		floating point instructions, cmp/str, and more moves.
		Adding a dummy dev_pvr (Dreamcast graphics controller).
20061014	Generalizing the expression evaluator (used in the built-in
		debugger) to support parentheses and +-*/%^&|.
20061015	Removing the experimental tlb index hint code in
		mips_memory_v2p.c, since it didn't really have any effect.
20061017	Minor SH updates; adding the "sts pr,Rn", fcmp/gt, fneg,
		frchg, and some other instructions. Fixing missing sign-
		extension in an 8-bit load instruction.
20061019	Adding a simple dev_dreamcast_rtc.
		Implementing memory-mapped access to the SH ITLB/UTLB arrays.
20061021	Continuing on various SH and Dreamcast things: sh4 timers,
		debug messages for dev_pvr, fixing some virtual address
		translation bugs, adding the bsrf instruction.
		The NetBSD/dreamcast GENERIC_MD kernel now reaches userland :)
		Adding a dummy dev_dreamcast_asic.c (not really useful yet).
		Implementing simple support for Store Queues.
		Beginning on the PVR Tile Accelerator.
20061022	Generalizing the PVR framebuffer to support off-screen drawing,
		multiple bit-depths, etc. (A small speed penalty, but most
		likely worth it.)
		Adding more SH instructions (mulu.w, fcmp/eq, fsub, fmac,
		fschg, and some more); correcting bugs in "fsca" and "float".
20061024	Adding the SH ftrv (matrix * vector) instruction. Marcus
		Comstedt's "tatest" example runs :) (wireframe only).
		Correcting disassembly for SH floating point instructions that
		use the xd* registers.
		Adding the SH fsts instruction.
		In memory_device_dyntrans_access(), only the currently used
		range is now invalidated, and not the entire device range.
20061025	Adding a dummy AVR32 cpu mode skeleton.
20061026	Various Dreamcast updates; beginning on a Maple bus controller.
20061027	Continuing on the Maple bus. A bogus Controller, Keyboard, and
		Mouse can now be detected by NetBSD and KOS homebrew programs.
		Cleaning up the SH4 Timer Management Unit, and beginning on
		SH4 interrupts.
		Implementing the Dreamcast SYSASIC.
20061028	Continuing on the SYSASIC.
		Adding the SH fsqrt instruction.
		memory_sh.c now actually scans the ITLB.
		Fixing a bug in dev_sh4.c, related to associative writes into
		the memory-mapped UTLB array. NetBSD/dreamcast now reaches
		userland stably, and prints the "Terminal type?" message :-]
		Implementing enough of the Dreamcast keyboard to make NetBSD
		accept it for input.
		Enabling SuperH for stable (non-development) builds.
		Adding NetBSD/dreamcast to the documentation, although it
		doesn't support root-on-nfs yet.
20061029	Changing usleep(1) calls in the debugger to to usleep(10000)
		(according to Brian Foley, this makes GXemul run better on
		MacOS X).
		Making the Maple "Controller" do something (enough to barely
		interact with dcircus.elf).
20061030-31	Some progress on the PVR. More test programs start running (but
		with strange output).
		Various other SH4-related updates.
20061102	Various Dreamcast and SH4 updates; more KOS demos run now.
20061104	Adding a skeleton dev_mb8696x.c (the Dreamcast's LAN adapter).
20061105	Continuing on the MB8696x; NetBSD/dreamcast detects it as mbe0.
		Testing for the release.

==============  RELEASE 0.4.3  ==============


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 32 * $Id: dev_mc146818.c,v 1.91 2006/10/07 03:20:19 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     int irq_nr;
80     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     if (pti > 800 && !warned) {
124     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 32 cpu_interrupt(cpu, d->irq_nr);
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     cpu_interrupt_ack(cpu, d->irq_nr);
476     }
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     cpu_interrupt_ack(cpu, d->irq_nr);
570 dpavlin 32
571     /*
572     * Acknowledging an interrupt decreases the
573     * number of pending "real world" timer ticks.
574     */
575     if (d->reg[MC_REGC * 4] & MC_REGC_PF)
576     d->pending_timer_interrupts --;
577    
578 dpavlin 4 d->reg[MC_REGC * 4] = 0x00;
579     }
580     }
581    
582     #ifdef MC146818_DEBUG
583     if (writeflag == MEM_READ) {
584     fatal("[ mc146818: read from addr=0x%04x (len %i): ",
585     (int)relative_addr, (int)len);
586     for (i=0; i<len; i++)
587 dpavlin 20 fatal("0x%02x ", data[i]);
588 dpavlin 4 fatal("]\n");
589     }
590     #endif
591    
592     return 1;
593     }
594    
595    
596     /*
597     * dev_mc146818_init():
598     *
599     * This needs to work for both DECstation emulation and other machine types,
600     * so it contains both rtc related stuff and the station's Ethernet address.
601     */
602     void dev_mc146818_init(struct machine *machine, struct memory *mem,
603     uint64_t baseaddr, int irq_nr, int access_style, int addrdiv)
604     {
605     unsigned char ether_address[6];
606     int i, dev_len;
607     struct mc_data *d;
608    
609     d = malloc(sizeof(struct mc_data));
610     if (d == NULL) {
611     fprintf(stderr, "out of memory\n");
612     exit(1);
613     }
614    
615     memset(d, 0, sizeof(struct mc_data));
616     d->irq_nr = irq_nr;
617     d->access_style = access_style;
618     d->addrdiv = addrdiv;
619    
620 dpavlin 6 d->use_bcd = 0;
621 dpavlin 20 switch (access_style) {
622     case MC146818_SGI:
623     case MC146818_PC_CMOS:
624     case MC146818_PMPPC:
625 dpavlin 6 d->use_bcd = 1;
626 dpavlin 20 }
627 dpavlin 6
628 dpavlin 20 if (machine->machine_type != MACHINE_PREP) {
629     /* NetBSD/prep has a really ugly clock detection code;
630 dpavlin 22 no other machines/OSes don't need this. */
631 dpavlin 20 d->ugly_netbsd_prep_hack_done = NETBSD_HACK_DONE;
632     }
633    
634 dpavlin 6 if (access_style == MC146818_DEC) {
635 dpavlin 4 /* Station Ethernet Address, on DECstation 3100: */
636     for (i=0; i<6; i++)
637     ether_address[i] = 0x10 * (i+1);
638    
639 dpavlin 6 d->reg[0x01] = ether_address[0];
640     d->reg[0x05] = ether_address[1];
641     d->reg[0x09] = ether_address[2];
642     d->reg[0x0d] = ether_address[3];
643     d->reg[0x11] = ether_address[4];
644     d->reg[0x15] = ether_address[5];
645     /* TODO: 19, 1d, 21, 25 = checksum bytes 1,2,2,1 resp. */
646     d->reg[0x29] = ether_address[5];
647     d->reg[0x2d] = ether_address[4];
648     d->reg[0x31] = ether_address[3];
649     d->reg[0x35] = ether_address[2];
650     d->reg[0x39] = ether_address[1];
651     d->reg[0x3d] = ether_address[1];
652     d->reg[0x41] = ether_address[0];
653     d->reg[0x45] = ether_address[1];
654     d->reg[0x49] = ether_address[2];
655     d->reg[0x4d] = ether_address[3];
656     d->reg[0x51] = ether_address[4];
657     d->reg[0x55] = ether_address[5];
658     /* TODO: 59, 5d = checksum bytes 1,2 resp. */
659     d->reg[0x61] = 0xff;
660     d->reg[0x65] = 0x00;
661     d->reg[0x69] = 0x55;
662     d->reg[0x6d] = 0xaa;
663     d->reg[0x71] = 0xff;
664     d->reg[0x75] = 0x00;
665     d->reg[0x79] = 0x55;
666     d->reg[0x7d] = 0xaa;
667 dpavlin 4
668     /* Battery valid, for DECstations */
669     d->reg[0xf8] = 1;
670     }
671    
672 dpavlin 20 /*
673     * uip_threshold should ideally be 1, but when Linux polls the UIP bit
674     * it looses speed. This hack gives Linux the impression that the cpu
675     * is uip_threshold times faster than the slow clock it would
676     * otherwise detect.
677     *
678     * TODO: Find out if this messes up Sprite emulation; if so, then
679     * this hack has to be removed.
680     */
681 dpavlin 22 d->uip_threshold = 8;
682 dpavlin 20
683 dpavlin 4 if (access_style == MC146818_ARC_JAZZ)
684     memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,
685 dpavlin 20 1, dev_mc146818_jazz_access, d, DM_DEFAULT, NULL);
686 dpavlin 4
687     dev_len = DEV_MC146818_LENGTH;
688     switch (access_style) {
689 dpavlin 16 case MC146818_CATS:
690 dpavlin 4 case MC146818_PC_CMOS:
691     dev_len = 2;
692     break;
693     case MC146818_SGI:
694     dev_len = 0x400;
695     }
696    
697     memory_device_register(mem, "mc146818", baseaddr,
698     dev_len * addrdiv, dev_mc146818_access,
699 dpavlin 20 d, DM_DEFAULT, NULL);
700 dpavlin 4
701     mc146818_update_time(d);
702    
703 dpavlin 24 machine_add_tickfunction(machine, dev_mc146818_tick, d,
704     TICK_SHIFT, 0.0);
705 dpavlin 4 }
706    

  ViewVC Help
Powered by ViewVC 1.1.26