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