1 /*
2   Hatari - nvram.c
3 
4   This file is distributed under the GNU General Public License, version 2
5   or at your option any later version. Read the file gpl.txt for details.
6 
7   This file is partly based on GPL code taken from the Aranym project.
8   - Copyright (c) 2001-2004 Petr Stehlik of ARAnyM dev team
9   - Adaption to Hatari (c) 2006 by Thomas Huth
10 
11   Atari TT and Falcon NVRAM/RTC emulation code.
12   This is a MC146818A or compatible chip with a non-volatile RAM area.
13 
14   These are the important bytes in the nvram array:
15 
16   Byte:    Description:
17 
18   14-15    Preferred operating system (TOS, Unix)
19    20      Language
20    21      Keyboard layout
21    22      Format of date/time
22    23      Separator for date
23    24      Boot delay
24   28-29    Video mode
25    30      SCSI-ID in bits 0-2, bus arbitration flag in bit 7 (1=off, 0=on)
26   62-63    Checksum
27 
28   All other cells are reserved / unused.
29 */
30 const char NvRam_fileid[] = "Hatari nvram.c : " __DATE__ " " __TIME__;
31 
32 #include "main.h"
33 #include "configuration.h"
34 #include "ioMem.h"
35 #include "log.h"
36 #include "nvram.h"
37 #include "paths.h"
38 #include "vdi.h"
39 
40 
41 // Defs for checksum
42 #define CKS_RANGE_START	14
43 #define CKS_RANGE_END	(14+47)
44 #define CKS_RANGE_LEN	(CKS_RANGE_END-CKS_RANGE_START+1)
45 #define CKS_LOC			(14+48)
46 
47 #define NVRAM_START  14
48 #define NVRAM_LEN    50
49 
50 static Uint8 nvram[64] = { 48,255,21,255,23,255,1,25,3,33,42,14,112,128,
51 	0,0,0,0,0,0,0,0,17,46,32,1,255,0,1,10,135,0,0,0,0,0,0,0,
52 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
53 
54 
55 static Uint8 nvram_index;
56 static char nvram_filename[FILENAME_MAX];
57 
58 
59 /*-----------------------------------------------------------------------*/
60 /**
61  * Load NVRAM data from file.
62  */
NvRam_Load(void)63 static bool NvRam_Load(void)
64 {
65 	bool ret = false;
66 	FILE *f = fopen(nvram_filename, "rb");
67 	if (f != NULL)
68 	{
69 		Uint8 fnvram[NVRAM_LEN];
70 		if (fread(fnvram, 1, NVRAM_LEN, f) == NVRAM_LEN)
71 		{
72 			memcpy(nvram+NVRAM_START, fnvram, NVRAM_LEN);
73 			LOG_TRACE(TRACE_NVRAM, "NVRAM: loaded from '%s'\n", nvram_filename);
74 			ret = true;
75 		}
76 		else
77 		{
78 			Log_Printf(LOG_WARN, "NVRAM loading from '%s' failed\n", nvram_filename);
79 		}
80 		fclose(f);
81 	}
82 	else
83 	{
84 		Log_Printf(LOG_INFO, "NVRAM not found at '%s'\n", nvram_filename);
85 	}
86 
87 	return ret;
88 }
89 
90 
91 /*-----------------------------------------------------------------------*/
92 /**
93  * Save NVRAM data to file
94  */
NvRam_Save(void)95 static bool NvRam_Save(void)
96 {
97 	bool ret = false;
98 	FILE *f = fopen(nvram_filename, "wb");
99 	if (f != NULL)
100 	{
101 		if (fwrite(nvram+NVRAM_START, 1, NVRAM_LEN, f) == NVRAM_LEN)
102 		{
103 			LOG_TRACE(TRACE_NVRAM, "NVRAM: saved to '%s'\n", nvram_filename);
104 			ret = true;
105 		}
106 		else
107 		{
108 			Log_Printf(LOG_WARN, "Writing NVRAM to '%s' failed\n", nvram_filename);
109 		}
110 		fclose(f);
111 	}
112 	else
113 	{
114 		Log_Printf(LOG_WARN, "Storing NVRAM to '%s' failed\n", nvram_filename);
115 	}
116 
117 	return ret;
118 }
119 
120 
121 /*-----------------------------------------------------------------------*/
122 /**
123  * Create NVRAM checksum. The checksum is over all bytes except the
124  * checksum bytes themselves; these are at the very end.
125  */
NvRam_SetChecksum(void)126 static void NvRam_SetChecksum(void)
127 {
128 	int i;
129 	unsigned char sum = 0;
130 
131 	for(i = CKS_RANGE_START; i <= CKS_RANGE_END; ++i)
132 		sum += nvram[i];
133 	nvram[NVRAM_CHKSUM1] = ~sum;
134 	nvram[NVRAM_CHKSUM2] = sum;
135 }
136 
137 /*-----------------------------------------------------------------------*/
138 /**
139  * NvRam_Reset: Called during init and reset, used for resetting the
140  * emulated chip.
141  */
NvRam_Reset(void)142 void NvRam_Reset(void)
143 {
144 	if (bUseVDIRes)
145 	{
146 		/* The objective is to start the TOS with a video mode similar
147 		 * to the requested one. This is important for the TOS to initialize
148 		 * the right font height and palette. */
149 		if (VDIHeight < 400)
150 		{
151 			/* This will select the 8x8 system font */
152 			switch(VDIPlanes)
153 			{
154 			/* The case 1 is not handled, because that would result in 0x0000
155 			 * which is an invalide video mode. This does not matter,
156 			 * since any color palette is good for monochrome, anyway. */
157 			case 2:	/* set 320x200x4 colors */
158 				nvram[NVRAM_VMODE1] = 0x00;
159 				nvram[NVRAM_VMODE2] = 0x01;
160 				break;
161 			case 4:	/* set 320x200x16 colors */
162 			default:
163 				nvram[NVRAM_VMODE1] = 0x00;
164 				nvram[NVRAM_VMODE2] = 0x02;
165 			}
166 		}
167 		else
168 		{
169 			/* This will select the 8x16 system font */
170 			switch(VDIPlanes)
171 			{
172 			case 4:	/* set 640x400x16 colors */
173 				nvram[NVRAM_VMODE1] = 0x01;
174 				nvram[NVRAM_VMODE2] = 0x0a;
175 				break;
176 			case 2:	/* set 640x400x4 colors */
177 				nvram[NVRAM_VMODE1] = 0x01;
178 				nvram[NVRAM_VMODE2] = 0x09;
179 				break;
180 			case 1:	/* set 640x400x2 colors */
181 			default:
182 				nvram[NVRAM_VMODE1] = 0x01;
183 				nvram[NVRAM_VMODE2] = 0x08;
184 			}
185 		}
186 		NvRam_SetChecksum();
187 	}
188 	nvram_index = 0;
189 }
190 
191 /*-----------------------------------------------------------------------*/
192 /**
193  * Initialization
194  */
NvRam_Init(void)195 void NvRam_Init(void)
196 {
197 	const char sBaseName[] = "hatari.nvram";
198 	const char *psHomeDir;
199 
200 	// set up the nvram filename
201 	psHomeDir = Paths_GetHatariHome();
202 	if (strlen(psHomeDir)+sizeof(sBaseName)+1 < sizeof(nvram_filename))
203 		sprintf(nvram_filename, "%s%c%s", psHomeDir, PATHSEP, sBaseName);
204 	else
205 		strcpy(nvram_filename, sBaseName);
206 
207 	if (!NvRam_Load())		// load NVRAM file automatically
208 	{
209 		if (ConfigureParams.Screen.nMonitorType == MONITOR_TYPE_VGA)   // VGA ?
210 		{
211 			nvram[NVRAM_VMODE1] &= ~0x01;		// No doublescan
212 			nvram[NVRAM_VMODE2] |= 0x10;		// VGA mode
213 			nvram[NVRAM_VMODE2] &= ~0x20;		// 60 Hz
214 		}
215 		else
216 		{
217 			nvram[NVRAM_VMODE1] |= 0x01;		// Interlaced
218 			nvram[NVRAM_VMODE2] &= ~0x10;		// TV/RGB mode
219 			nvram[NVRAM_VMODE2] |= 0x20;		// 50 Hz
220 		}
221 		NvRam_SetChecksum();
222 	}
223 
224 	NvRam_Reset();
225 }
226 
227 
228 /*-----------------------------------------------------------------------*/
229 /**
230  * De-Initialization
231  */
NvRam_UnInit(void)232 void NvRam_UnInit(void)
233 {
234 	NvRam_Save();		// save NVRAM file upon exit automatically (should be conditionalized)
235 }
236 
237 
238 /*-----------------------------------------------------------------------*/
239 /**
240  * Read from RTC/NVRAM offset selection register ($ff8961)
241  */
NvRam_Select_ReadByte(void)242 void NvRam_Select_ReadByte(void)
243 {
244 	IoMem_WriteByte(0xff8961, nvram_index);
245 }
246 
247 
248 /*-----------------------------------------------------------------------*/
249 /**
250  * Write to RTC/NVRAM offset selection register ($ff8961)
251  */
NvRam_Select_WriteByte(void)252 void NvRam_Select_WriteByte(void)
253 {
254 	Uint8 value = IoMem_ReadByte(0xff8961);
255 
256 	if (value < sizeof(nvram))
257 	{
258 		nvram_index = value;
259 	}
260 	else
261 	{
262 		Log_Printf(LOG_WARN, "NVRAM: trying to set out-of-bound position (%d)\n", value);
263 	}
264 }
265 
266 
267 /*-----------------------------------------------------------------------*/
268 /**
269  * Read from RTC/NVRAM data register ($ff8963)
270  */
NvRam_Data_ReadByte(void)271 void NvRam_Data_ReadByte(void)
272 {
273 	Uint8 value = 0;
274 
275 	if (nvram_index == NVRAM_SECONDS || nvram_index == NVRAM_MINUTES || nvram_index == NVRAM_HOURS
276 	    || (nvram_index >=NVRAM_DAY && nvram_index <=NVRAM_YEAR) )
277 	{
278 		/* access to RTC?  - then read host clock and return its values */
279 		time_t tim = time(NULL);
280 		struct tm *curtim = localtime(&tim);	/* current time */
281 		switch(nvram_index)
282 		{
283 			case NVRAM_SECONDS: value = curtim->tm_sec; break;
284 			case NVRAM_MINUTES: value = curtim->tm_min; break;
285 			case NVRAM_HOURS: value = curtim->tm_hour; break;
286 			case NVRAM_DAY: value = curtim->tm_mday; break;
287 			case NVRAM_MONTH: value = curtim->tm_mon+1; break;
288 			case NVRAM_YEAR: value = curtim->tm_year - 68; break;
289 		}
290 	}
291 	else if (nvram_index == 10)
292 	{
293 		static bool rtc_uip = true;
294 		value = rtc_uip ? 0x80 : 0;
295 		rtc_uip = !rtc_uip;
296 	}
297 	else if (nvram_index == 13)
298 	{
299 		value = 0x80;   // Valid RAM and Time bit
300 	}
301 	else if (nvram_index < 14)
302 	{
303 		Log_Printf(LOG_DEBUG, "Read from unsupported RTC/NVRAM register 0x%x.\n", nvram_index);
304 		value = nvram[nvram_index];
305 	}
306 	else
307 	{
308 		value = nvram[nvram_index];
309 	}
310 
311 	LOG_TRACE(TRACE_NVRAM, "NVRAM: read data at %d = %d ($%02x)\n", nvram_index, value, value);
312 	IoMem_WriteByte(0xff8963, value);
313 }
314 
315 
316 /*-----------------------------------------------------------------------*/
317 /**
318  * Write to RTC/NVRAM data register ($ff8963)
319  */
320 
NvRam_Data_WriteByte(void)321 void NvRam_Data_WriteByte(void)
322 {
323 	Uint8 value = IoMem_ReadByte(0xff8963);
324 	LOG_TRACE(TRACE_NVRAM, "NVRAM: write data at %d = %d ($%02x)\n", nvram_index, value, value);
325 	nvram[nvram_index] = value;
326 }
327