1 /*
2 * libtilemcore - Graphing calculator emulation library
3 *
4 * Copyright (C) 2001 Solignac Julien
5 * Copyright (C) 2004-2012 Benjamin Moody
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include "tilem.h"
28
29 #define FLASH_READ 0
30 #define FLASH_AA 1
31 #define FLASH_55 2
32 #define FLASH_PROG 3
33 #define FLASH_ERASE 4
34 #define FLASH_ERAA 5
35 #define FLASH_ER55 6
36 #define FLASH_ERROR 7
37 #define FLASH_FASTMODE 8
38 #define FLASH_FASTPROG 9
39 #define FLASH_FASTEXIT 10
40
41 #define FLASH_BUSY_PROGRAM 1
42 #define FLASH_BUSY_ERASE_WAIT 2
43 #define FLASH_BUSY_ERASE 3
44
45 /* Still to do:
46 - autoselect
47 - erase suspend
48 - fast program
49 - CFI
50 */
51
52 #define WARN(xxx) \
53 tilem_warning(calc, "Flash error (" xxx ")")
54 #define WARN2(xxx, yyy, zzz) \
55 tilem_warning(calc, "Flash error (" xxx ")", (yyy), (zzz))
56
tilem_flash_reset(TilemCalc * calc)57 void tilem_flash_reset(TilemCalc* calc)
58 {
59 calc->flash.unlock = 0;
60 calc->flash.state = FLASH_READ;
61 calc->flash.busy = 0;
62 }
63
tilem_flash_delay_timer(TilemCalc * calc,void * data TILEM_ATTR_UNUSED)64 void tilem_flash_delay_timer(TilemCalc* calc, void* data TILEM_ATTR_UNUSED)
65 {
66 if (calc->flash.busy == FLASH_BUSY_ERASE_WAIT) {
67 calc->flash.busy = FLASH_BUSY_ERASE;
68 tilem_z80_set_timer(calc, TILEM_TIMER_FLASH_DELAY,
69 200000, 0, 1);
70 }
71 else {
72 calc->flash.busy = 0;
73 }
74 }
75
76 #ifdef DISABLE_FLASH_DELAY
77 # define set_busy(fl, bm, t)
78 #else
set_busy(TilemCalc * calc,int busymode,int time)79 static inline void set_busy(TilemCalc* calc, int busymode, int time)
80 {
81 if (!(calc->flash.emuflags & TILEM_FLASH_REQUIRE_DELAY))
82 return;
83
84 calc->flash.busy = busymode;
85 tilem_z80_set_timer(calc, TILEM_TIMER_FLASH_DELAY,
86 time, 0, 1);
87 }
88 #endif
89
program_byte(TilemCalc * calc,dword a,byte v)90 static inline void program_byte(TilemCalc* calc, dword a, byte v)
91 {
92 calc->mem[a] &= v;
93 calc->flash.progaddr = a;
94 calc->flash.progbyte = v;
95
96 if (calc->mem[a] != v) {
97 WARN2("bad program %02x over %02x", v, calc->mem[a]);
98 calc->flash.state = FLASH_ERROR;
99 }
100 else {
101 calc->flash.state = FLASH_READ;
102 }
103
104 set_busy(calc, FLASH_BUSY_PROGRAM, 7);
105 }
106
erase_sector(TilemCalc * calc,dword a,dword l)107 static inline void erase_sector(TilemCalc* calc, dword a, dword l)
108 {
109 dword i;
110
111 calc->flash.progaddr = a;
112 for (i = 0; i < l; i++)
113 calc->mem[a + i]=0xFF;
114 calc->flash.state = FLASH_READ;
115
116 set_busy(calc, FLASH_BUSY_ERASE_WAIT, 50);
117 }
118
get_sector(TilemCalc * calc,dword pa)119 static const TilemFlashSector* get_sector(TilemCalc* calc, dword pa)
120 {
121 int i;
122 const TilemFlashSector* sec;
123
124 for (i = 0; i < calc->hw.nflashsectors; i++) {
125 sec = &calc->hw.flashsectors[i];
126 if (pa >= sec->start && pa < sec->start + sec->size)
127 return sec;
128 }
129
130 return NULL;
131 }
132
sector_writable(TilemCalc * calc,const TilemFlashSector * sec)133 static int sector_writable(TilemCalc* calc, const TilemFlashSector* sec)
134 {
135 return !(sec->protectgroup & ~calc->flash.overridegroup);
136 }
137
tilem_flash_read_byte(TilemCalc * calc,dword pa)138 byte tilem_flash_read_byte(TilemCalc* calc, dword pa)
139 {
140 byte value;
141
142 if (calc->flash.busy == FLASH_BUSY_PROGRAM) {
143 if (pa != calc->flash.progaddr)
144 WARN("reading from Flash while programming");
145 value = (~calc->flash.progbyte & 0x80);
146 value |= calc->flash.toggles;
147 calc->flash.toggles ^= 0x40;
148 return (value);
149 }
150 else if (calc->flash.busy == FLASH_BUSY_ERASE) {
151 if ((pa >> 16) != (calc->flash.progaddr >> 16))
152 WARN("reading from Flash while erasing");
153 value = calc->flash.toggles | 0x08;
154 calc->flash.toggles ^= 0x44;
155 return (value);
156 }
157 else if (calc->flash.busy == FLASH_BUSY_ERASE_WAIT) {
158 if ((pa >> 16) != (calc->flash.progaddr >> 16))
159 WARN("reading from Flash while erasing");
160 value = calc->flash.toggles;
161 calc->flash.toggles ^= 0x44;
162 return (value);
163 }
164
165 if (calc->flash.state == FLASH_ERROR) {
166 value = ((~calc->flash.progbyte & 0x80) | 0x20);
167 value |= calc->flash.toggles;
168 calc->flash.toggles ^= 0x40;
169 return (value);
170 }
171 else if (calc->flash.state == FLASH_FASTMODE) {
172 return (calc->mem[pa]);
173 }
174 else if (calc->flash.state == FLASH_READ) {
175 return (calc->mem[pa]);
176 }
177 else {
178 WARN("reading during program/erase sequence");
179 calc->flash.state = FLASH_READ;
180 return (calc->mem[pa]);
181 }
182 }
183
tilem_flash_erase_address(TilemCalc * calc,dword pa)184 void tilem_flash_erase_address(TilemCalc* calc, dword pa)
185 {
186 const TilemFlashSector* sec = get_sector(calc, pa);
187
188 if (sector_writable(calc, sec)) {
189 tilem_message(calc, "Erasing Flash sector at %06x", pa);
190 erase_sector(calc, sec->start, sec->size);
191 }
192 else {
193 WARN("erasing protected sector");
194 }
195 }
196
tilem_flash_write_byte(TilemCalc * calc,dword pa,byte v)197 void tilem_flash_write_byte(TilemCalc* calc, dword pa, byte v)
198 {
199 int oldstate;
200 int i;
201 const TilemFlashSector* sec;
202
203 if (!calc->flash.unlock)
204 return;
205
206 #ifndef DISABLE_FLASH_DELAY
207 if (calc->flash.busy == FLASH_BUSY_PROGRAM
208 || calc->flash.busy == FLASH_BUSY_ERASE)
209 return;
210 #endif
211
212 oldstate = calc->flash.state;
213 calc->flash.state = FLASH_READ;
214
215 switch (oldstate) {
216 case FLASH_READ:
217 if (((pa&0xFFF) == 0xAAA) && (v == 0xAA))
218 calc->flash.state = FLASH_AA;
219 return;
220
221 case FLASH_AA:
222 if (((pa&0xFFF) == 0x555) && (v == 0x55))
223 calc->flash.state = FLASH_55;
224 else if (v != 0xF0) {
225 WARN2("undefined command %02x->%06x after AA", v, pa);
226 }
227 return;
228
229 case FLASH_55:
230 if ((pa&0xFFF) == 0xAAA) {
231 switch (v) {
232 case 0x10:
233 case 0x30:
234 WARN("attempt to erase without pre-erase");
235 return;
236 case 0x20:
237 //WARN("entering fast mode");
238 calc->flash.state = FLASH_FASTMODE;
239 return;
240 case 0x80:
241 calc->flash.state = FLASH_ERASE;
242 return;
243 case 0x90:
244 WARN("autoselect is not implemented");
245 return;
246 case 0xA0:
247 calc->flash.state = FLASH_PROG;
248 return;
249 }
250 }
251 if (v != 0xF0)
252 WARN2("undefined command %02x->%06x after AA,55", v, pa);
253 return;
254
255 case FLASH_PROG:
256 sec = get_sector(calc, pa);
257 if (!sector_writable(calc, sec))
258 WARN("programming protected sector");
259 else
260 program_byte(calc, pa, v);
261 return;
262
263 case FLASH_FASTMODE:
264 //WARN2("fast mode cmd %02x->%06x", v, pa);
265 if ( v == 0x90 )
266 calc->flash.state = FLASH_FASTEXIT;
267 else if ( v == 0xA0 )
268 calc->flash.state = FLASH_FASTPROG;
269 else
270 // TODO : figure out whether mixing is allowed on real HW
271 WARN2("mixing fast programming with regular programming : %02x->%06x", v, pa);
272 return;
273
274 case FLASH_FASTPROG:
275 //WARN2("fast prog %02x->%06x", v, pa);
276 sec = get_sector(calc, pa);
277 if (!sector_writable(calc, sec))
278 WARN("programming protected sector");
279 else
280 program_byte(calc, pa, v);
281 calc->flash.state = FLASH_FASTMODE;
282 return;
283
284 case FLASH_FASTEXIT:
285 //WARN("leaving fast mode");
286 if ( v != 0xF0 )
287 {
288 WARN2("undefined command %02x->%06x after fast mode pre-exit 90", v, pa);
289 // TODO : figure out whether fast mode remains in such a case
290 calc->flash.state = FLASH_FASTMODE;
291 }
292 return;
293
294 case FLASH_ERASE:
295 if (((pa&0xFFF) == 0xAAA) && (v == 0xAA))
296 calc->flash.state = FLASH_ERAA;
297 else if (v != 0xF0)
298 WARN2("undefined command %02x->%06x after pre-erase", v, pa);
299 return;
300
301 case FLASH_ERAA:
302 if (((pa&0xFFF) == 0x555) && (v == 0x55))
303 calc->flash.state = FLASH_ER55;
304 else if (v != 0xF0)
305 WARN2("undefined command %02x->%06x after pre-erase AA", v, pa);
306 return;
307
308 case FLASH_ER55:
309 if (((pa&0xFFF) == 0xAAA) && v==0x10) {
310 tilem_message(calc, "Erasing entire Flash chip");
311
312 for (i = 0; i < calc->hw.nflashsectors; i++) {
313 sec = &calc->hw.flashsectors[i];
314 if (sector_writable(calc, sec))
315 erase_sector(calc, sec->start,
316 sec->size);
317 }
318 }
319 else if (v == 0x30) {
320 tilem_flash_erase_address(calc, pa);
321 }
322 else if (v != 0xF0)
323 WARN2("undefined command %02x->%06x after pre-erase AA,55", v, pa);
324 return;
325 }
326 }
327