1 /*
2  * rtc.cpp - Atari NVRAM emulation code
3  *
4  * Copyright (c) 2001-2006 Petr Stehlik of ARAnyM dev team (see AUTHORS)
5  *
6  * This file is part of the ARAnyM project which builds a new and powerful
7  * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
8  *
9  * ARAnyM is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * ARAnyM is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with ARAnyM; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #include "sysdeps.h"
25 #include "hardware.h"
26 #include "cpu_emulation.h"
27 #include "memory-uae.h"
28 #include "rtc.h"
29 #include "parameters.h"
30 
31 #define DEBUG 0
32 #include "debug.h"
33 
34 #define CKS_RANGE_START	14
35 #define CKS_RANGE_END	(14+47)
36 #define CKS_RANGE_LEN	(CKS_RANGE_END-CKS_RANGE_START+1)
37 #define CKS_LOC			(14+48)
38 
39 uint8 nvram[64]={48,255,21,255,23,255,1,25,3,33,42,14,112,128,
40 		0,0,0,0,0,0,0,0,17,46,32,1,255,0,0,56,135,0,0,0,0,0,0,0,
41         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,31};
42 
43 #define NVRAM_SYSTEM_LANGUAGE	20
44 #define NVRAM_KEYBOARD_LAYOUT	21
45 
46 /*
47 int byte15th = (colors & 7) | (80:40) << 3 | (VGA : TV) << 4 | (PAL : NTSC) << 5| overscan << 6 | STcompatible << 7);
48 int byte14th = VGA:TV ? line doubling : half screen;
49 */
50 
RTC(memptr addr,uint32 size)51 RTC::RTC(memptr addr, uint32 size) : BASE_IO(addr, size)
52 {
53 	init();
54 	reset();
55 }
56 
reset()57 void RTC::reset()
58 {
59 	patch();
60 	index = 0;
61 }
62 
~RTC()63 RTC::~RTC()
64 {
65 	save();		// save NVRAM file upon exit automatically (should be conditionalized)
66 }
67 
init()68 void RTC::init()
69 {
70 	// set up the nvram filename
71 	getConfFilename(ARANYMNVRAM, nvram_filename, sizeof(nvram_filename));
72 
73 	load();		// load NVRAM file automatically
74 
75 }
76 
patch()77 void RTC::patch()
78 {
79 	if (bx_options.video.monitor != -1) {
80 		if (bx_options.video.monitor == 0)	// VGA
81 			nvram[29] |= 0x10;		// the monitor type should be set on a working copy only
82 		else
83 			nvram[29] &= ~0x10;
84 	}
85 	if (bx_options.video.boot_color_depth != -1) {
86 		int res = nvram[29] & 0x07;
87 		switch(bx_options.video.boot_color_depth) {
88 			case 1: res = 0; break;
89 			case 2: res = 1; break;
90 			case 4: res = 2; break;
91 			case 8: res = 3; break;
92 			case 16: res = 4; break;
93 		}
94 		nvram[29] &= ~0x07;
95 		nvram[29] |= res;		// the booting resolution should be set on a working copy only
96 	}
97 
98 	if (bx_options.tos.cookie_akp != -1) {
99 		nvram[NVRAM_SYSTEM_LANGUAGE] = bx_options.tos.cookie_akp & 0xff;
100 		nvram[NVRAM_KEYBOARD_LAYOUT] = (bx_options.tos.cookie_akp >> 8) & 0xff;
101 	}
102 
103 	setChecksum();
104 }
105 
load()106 bool RTC::load()
107 {
108 	bool ret = false;
109 	FILE *f = fopen(nvram_filename, "rb");
110 	if (f != NULL) {
111 		uint8 fnvram[CKS_RANGE_LEN];
112 		if (fread(fnvram, 1, CKS_RANGE_LEN, f) == CKS_RANGE_LEN) {
113 			memcpy(nvram+CKS_RANGE_START, fnvram, CKS_RANGE_LEN);
114 			ret = true;
115 		}
116 		fclose(f);
117 		if (false /*verbose*/) infoprint("NVRAM loaded from '%s'", nvram_filename);
118 	}
119 	else {
120 		panicbug("NVRAM not found at '%s'", nvram_filename);
121 	}
122 
123 	return ret;
124 }
125 
save()126 bool RTC::save()
127 {
128 	bool ret = false;
129 	FILE *f = fopen(nvram_filename, "wb");
130 	if (f != NULL) {
131 		if (fwrite(nvram+CKS_RANGE_START, 1, CKS_RANGE_LEN, f) == CKS_RANGE_LEN) {
132 			ret = true;
133 		}
134 		fclose(f);
135 	}
136 	else {
137 		panicbug("ERROR: cannot store NVRAM to '%s'", nvram_filename);
138 	}
139 
140 	return ret;
141 }
142 
handleRead(memptr addr)143 uint8 RTC::handleRead(memptr addr)
144 {
145 	addr -= getHWoffset();
146 	if (addr > getHWsize())
147 		return 0;
148 
149 	switch(addr) {
150 		case 1: return index;
151 		case 3: return getData();
152 	}
153 
154 	return 0;
155 }
156 
handleWrite(memptr addr,uint8 value)157 void RTC::handleWrite(memptr addr, uint8 value)
158 {
159 	addr -= getHWoffset();
160 	if (addr > getHWsize())
161 		return;
162 
163 	switch(addr) {
164 		case 1: setAddr(value); break;
165 		case 3: setData(value); break;
166 	}
167 }
168 
setAddr(uint8 value)169 void RTC::setAddr(uint8 value)
170 {
171 	if (value < sizeof(nvram)) {
172 		index = value;
173 	}
174 	else {
175 		D(bug("NVRAM: trying to set out-of-bound position (%d)", value));
176 	}
177 }
178 
freezeTime(void)179 void RTC::freezeTime(void)
180 {
181 	time_t tim = time(NULL);
182 	frozen_time = *(bx_options.gmtime ? gmtime(&tim) : localtime(&tim));
183 }
184 
getFrozenTime(void)185 struct tm RTC::getFrozenTime(void)
186 {
187 	if (!(nvram[11] & 0x80))
188 		freezeTime();
189 	return frozen_time;
190 }
191 
getData()192 uint8 RTC::getData()
193 {
194 	uint8 value;
195 
196 #define BIN_TO_BCD() \
197 	if (!(nvram[11] & 0x04)) \
198 		value = ((value / 10) << 4) | (value % 10)
199 
200 	switch(index) {
201 	case 0:
202 		value = getFrozenTime().tm_sec;
203 		BIN_TO_BCD();
204 		break;
205 	case 2:
206 		value = getFrozenTime().tm_min;
207 		BIN_TO_BCD();
208 		break;
209 	case 4:
210 		value = getFrozenTime().tm_hour;
211 		if (!(nvram[11] & 0x02))
212 		{
213 			uint8 pmflag = (value == 0 || value >= 13) ? 0x80 : 0;
214 			value = value % 12;
215 			if (value == 0)
216 				value = 12;
217 			BIN_TO_BCD();
218 			value |= pmflag;
219 		} else
220 		{
221 			BIN_TO_BCD();
222 		}
223 		break;
224 	case 6:
225 		value = getFrozenTime().tm_wday + 1;
226 		BIN_TO_BCD();
227 		break;
228 	case 7:
229 		value = getFrozenTime().tm_mday;
230 		BIN_TO_BCD();
231 		break;
232 	case 8:
233 		value = getFrozenTime().tm_mon+1;
234 		BIN_TO_BCD();
235 		break;
236 	case 9:
237 		value = getFrozenTime().tm_year - 68;
238 		BIN_TO_BCD();
239 		break;
240 	case 1: /* alarm seconds */
241 	case 3:	/* alarm minutes */
242 	case 5: /* alarm hour */
243 		value = nvram[index];
244 		BIN_TO_BCD();
245 		break;
246 	case 10:
247 		nvram[index] ^= 0x80; // toggle UIP bit
248 		value = nvram[index];
249 		break;
250 	default:
251 		value = nvram[index];
252 		break;
253 	}
254 	D(bug("Reading NVRAM data at %d = %d ($%02x) at %06x", index, value, value, showPC()));
255 	return value;
256 }
257 
setData(uint8 value)258 void RTC::setData(uint8 value)
259 {
260 	D(bug("Writing NVRAM data at %d = %d ($%02x) at %06x", index, value, value, showPC()));
261 	switch (index)
262 	{
263 	case 11:
264 		if (value & 0x80)
265 			freezeTime();
266 		break;
267 	}
268 	nvram[index] = value;
269 }
270 
getNvramKeyboard()271 nvram_t RTC::getNvramKeyboard()
272 {
273 	/* Return keyboard language setting */
274 	return (nvram_t) nvram[NVRAM_KEYBOARD_LAYOUT];
275 }
276 
277 /* the checksum is over all bytes except the checksum bytes
278  * themselves; these are at the very end */
setChecksum()279 void RTC::setChecksum()
280 {
281 	int i;
282 	unsigned char sum = 0;
283 
284 	for(i = CKS_RANGE_START; i <= CKS_RANGE_END; ++i)
285 		sum += nvram[i];
286 	nvram[CKS_LOC] = ~sum;
287 	nvram[CKS_LOC+1] = sum;
288 }
289 
290 /*
291 vim:ts=4:sw=4:
292 */
293