1 /*
2  * flash040core.c - (AM)29F0[14]0(B) Flash emulation.
3  *
4  * Written by
5  *  Hannu Nuotio <hannu.nuotio@tut.fi>
6  * Extended by
7  *  Marko Makela <marko.makela@iki.fi>
8  *
9  * This file is part of VICE, the Versatile Commodore Emulator.
10  * See README for copyright notice.
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include "alarm.h"
34 #include "flash040.h"
35 #include "lib.h"
36 #include "log.h"
37 #include "maincpu.h"
38 #include "snapshot.h"
39 #include "types.h"
40 
41 /* -------------------------------------------------------------------------- */
42 
43 /* #define FLASH_DEBUG_ENABLED */
44 
45 #ifdef FLASH_DEBUG_ENABLED
46 #define FLASH_DEBUG(x) log_debug x
47 #else
48 #define FLASH_DEBUG(x)
49 #endif
50 
51 struct flash_types_s {
52     uint8_t manufacturer_ID;
53     uint8_t device_ID;
54     uint8_t device_ID_addr;
55     unsigned int size;
56     unsigned int sector_mask;
57     unsigned int sector_size;
58     unsigned int sector_shift;
59     unsigned int magic_1_addr;
60     unsigned int magic_2_addr;
61     unsigned int magic_1_mask;
62     unsigned int magic_2_mask;
63     uint8_t status_toggle_bits;
64     unsigned int erase_sector_timeout_cycles;
65     unsigned int erase_sector_cycles;
66     unsigned int erase_chip_cycles;
67 };
68 typedef struct flash_types_s flash_types_t;
69 
70 static const flash_types_t flash_types[FLASH040_TYPE_NUM] = {
71     /* AM29F040 */
72     { 0x01, 0xa4, 1,
73       0x80000,
74       0x70000, 0x10000, 16,
75       0x5555, 0x2aaa, 0x7fff, 0x7fff,
76       0x40,
77       80, 2000000, 14000000}, /* may take up to 30s and 120s */
78     /* AM29F040B */
79     { 0x01, 0xa4, 1,
80       0x80000,
81       0x70000, 0x10000, 16,
82       0x555, 0x2aa, 0x7ff, 0x7ff,
83       0x40,
84       50, 1000000, 8000000}, /* may take up to 8s and 64s */
85     /* 29F010 */
86     { 0x01, 0x20, 1,
87       0x20000,
88       0x1c000, 0x04000, 14,
89       0x5555, 0x2aaa, 0x7fff, 0x7fff,
90       0x40,
91       80, 1000000, 1000000 }, /* may take up to 15s */
92     /* 29F032B with A0/1 swap */
93     { 0x01, 0x41, 1,
94       0x400000,
95       0x3f0000, 0x10000, 16,
96       0x556, 0x2a9, 0x7ff, 0x7ff,
97       0x44,
98       50, 1000000, 64000000 }, /* may take up to 8s */
99     /* Spansion S29GL064N */
100     { 0x01, 0x7e, 2,
101       0x800000,
102       /* FIXME: some models support non-uniform sector layout */
103       0x7f0000, 0x10000, 16,
104       0xaaa, 0x555, 0xfff, 0xfff,
105       0x40,
106       50, 500000, 64000000}, /* may take up to 3.5s and 128s */
107 };
108 
109 /* -------------------------------------------------------------------------- */
110 
flash_magic_1(flash040_context_t * flash040_context,unsigned int addr)111 inline static int flash_magic_1(flash040_context_t *flash040_context, unsigned int addr)
112 {
113     return ((addr & flash_types[flash040_context->flash_type].magic_1_mask) == flash_types[flash040_context->flash_type].magic_1_addr);
114 }
115 
flash_magic_2(flash040_context_t * flash040_context,unsigned int addr)116 inline static int flash_magic_2(flash040_context_t *flash040_context, unsigned int addr)
117 {
118     return ((addr & flash_types[flash040_context->flash_type].magic_2_mask) == flash_types[flash040_context->flash_type].magic_2_addr);
119 }
120 
flash_clear_erase_mask(flash040_context_t * flash040_context)121 inline static void flash_clear_erase_mask(flash040_context_t *flash040_context)
122 {
123     int i;
124 
125     for (i = 0; i < FLASH040_ERASE_MASK_SIZE; ++i) {
126         flash040_context->erase_mask[i] = 0;
127     }
128 }
129 
flash_sector_to_addr(flash040_context_t * flash040_context,unsigned int sector)130 inline static unsigned int flash_sector_to_addr(flash040_context_t *flash040_context, unsigned int sector)
131 {
132     unsigned int sector_size = flash_types[flash040_context->flash_type].sector_size;
133 
134     return sector * sector_size;
135 }
136 
flash_addr_to_sector_number(flash040_context_t * flash040_context,unsigned int addr)137 inline static unsigned int flash_addr_to_sector_number(flash040_context_t *flash040_context, unsigned int addr)
138 {
139     unsigned int sector_addr = flash_types[flash040_context->flash_type].sector_mask & addr;
140     unsigned int sector_shift = flash_types[flash040_context->flash_type].sector_shift;
141 
142     return sector_addr >> sector_shift;
143 }
144 
flash_add_sector_to_erase_mask(flash040_context_t * flash040_context,unsigned int addr)145 inline static void flash_add_sector_to_erase_mask(flash040_context_t *flash040_context, unsigned int addr)
146 {
147     unsigned int sector_num = flash_addr_to_sector_number(flash040_context, addr);
148 
149     flash040_context->erase_mask[sector_num >> 3] |= (uint8_t)(1 << (sector_num & 0x7));
150 }
151 
flash_erase_sector(flash040_context_t * flash040_context,unsigned int sector)152 inline static void flash_erase_sector(flash040_context_t *flash040_context, unsigned int sector)
153 {
154     unsigned int sector_size = flash_types[flash040_context->flash_type].sector_size;
155     unsigned int sector_addr;
156 
157     sector_addr = flash_sector_to_addr(flash040_context, sector);
158 
159     FLASH_DEBUG(("Erasing 0x%x - 0x%x", sector_addr, sector_addr + sector_size - 1));
160     memset(&(flash040_context->flash_data[sector_addr]), 0xff, sector_size);
161     flash040_context->flash_dirty = 1;
162 }
163 
flash_erase_chip(flash040_context_t * flash040_context)164 inline static void flash_erase_chip(flash040_context_t *flash040_context)
165 {
166     FLASH_DEBUG(("Erasing chip"));
167     memset(flash040_context->flash_data, 0xff, flash_types[flash040_context->flash_type].size);
168     flash040_context->flash_dirty = 1;
169 }
170 
flash_program_byte(flash040_context_t * flash040_context,unsigned int addr,uint8_t byte)171 inline static int flash_program_byte(flash040_context_t *flash040_context, unsigned int addr, uint8_t byte)
172 {
173     uint8_t old_data = flash040_context->flash_data[addr];
174     uint8_t new_data = old_data & byte;
175 
176     FLASH_DEBUG(("Programming 0x%05x with 0x%02x (%02x->%02x)", addr, byte, old_data, old_data & byte));
177     flash040_context->program_byte = byte;
178     flash040_context->flash_data[addr] = new_data;
179     flash040_context->flash_dirty = 1;
180 
181     return (new_data == byte) ? 1 : 0;
182 }
183 
flash_write_operation_status(flash040_context_t * flash040_context)184 inline static int flash_write_operation_status(flash040_context_t *flash040_context)
185 {
186     return ((flash040_context->program_byte ^ 0x80) & 0x80)   /* DQ7 = inverse of programmed data */
187            | ((maincpu_clk & 2) << 5)                         /* DQ6 = toggle bit (2 us) */
188            | (1 << 5)                                         /* DQ5 = timeout */
189     ;
190 }
191 
flash_erase_operation_status(flash040_context_t * flash040_context)192 inline static int flash_erase_operation_status(flash040_context_t *flash040_context)
193 {
194     int v;
195 
196     /* DQ6 = toggle bit */
197     v = flash040_context->program_byte;
198 
199     /* toggle the toggle bit(s) */
200     /* FIXME better toggle bit II emulation */
201     flash040_context->program_byte ^= flash_types[flash040_context->flash_type].status_toggle_bits;
202 
203     /* DQ3 = sector erase timer */
204     if (flash040_context->flash_state != FLASH040_STATE_SECTOR_ERASE_TIMEOUT) {
205         v |= 0x08;
206     }
207 
208     return v;
209 }
210 
211 /* -------------------------------------------------------------------------- */
212 
erase_alarm_handler(CLOCK offset,void * data)213 static void erase_alarm_handler(CLOCK offset, void *data)
214 {
215     unsigned int i, j;
216     uint8_t m;
217     flash040_context_t *flash040_context = (flash040_context_t *)data;
218 
219     alarm_unset(flash040_context->erase_alarm);
220 
221     FLASH_DEBUG(("Erase alarm, state %i", (int)flash040_context->flash_state));
222 
223     switch (flash040_context->flash_state) {
224         case FLASH040_STATE_SECTOR_ERASE_TIMEOUT:
225             alarm_set(flash040_context->erase_alarm, maincpu_clk + flash_types[flash040_context->flash_type].erase_sector_cycles);
226             flash040_context->flash_state = FLASH040_STATE_SECTOR_ERASE;
227             break;
228         case FLASH040_STATE_SECTOR_ERASE:
229             for (i = 0; i < (8 * FLASH040_ERASE_MASK_SIZE); ++i) {
230                 j = i >> 3;
231                 m = (uint8_t)(1 << (i & 0x7));
232                 if (flash040_context->erase_mask[j] & m) {
233                     flash_erase_sector(flash040_context, i);
234                     flash040_context->erase_mask[j] &= (uint8_t) ~m;
235                     break;
236                 }
237             }
238 
239             for (i = 0, m = 0; i < FLASH040_ERASE_MASK_SIZE; ++i) {
240                 m |= flash040_context->erase_mask[i];
241             }
242 
243             if (m != 0) {
244                 alarm_set(flash040_context->erase_alarm, maincpu_clk + flash_types[flash040_context->flash_type].erase_sector_cycles);
245             } else {
246                 flash040_context->flash_state = flash040_context->flash_base_state;
247             }
248             break;
249 
250         case FLASH040_STATE_CHIP_ERASE:
251             flash_erase_chip(flash040_context);
252             flash040_context->flash_state = flash040_context->flash_base_state;
253             break;
254 
255         default:
256             FLASH_DEBUG(("Erase alarm - error, state %i unhandled!", (int)flash040_context->flash_state));
257             break;
258     }
259 }
260 
261 /* -------------------------------------------------------------------------- */
262 
flash040core_store_internal(flash040_context_t * flash040_context,unsigned int addr,uint8_t byte)263 static void flash040core_store_internal(flash040_context_t *flash040_context,
264                                         unsigned int addr, uint8_t byte)
265 {
266 #ifdef FLASH_DEBUG_ENABLED
267     flash040_state_t old_state = flash040_context->flash_state;
268     flash040_state_t old_base_state = flash040_context->flash_base_state;
269 #endif
270 
271     switch (flash040_context->flash_state) {
272         case FLASH040_STATE_READ:
273             if (flash_magic_1(flash040_context, addr) && (byte == 0xaa)) {
274                 flash040_context->flash_state = FLASH040_STATE_MAGIC_1;
275             }
276             break;
277 
278         case FLASH040_STATE_MAGIC_1:
279             if (flash_magic_2(flash040_context, addr) && (byte == 0x55)) {
280                 flash040_context->flash_state = FLASH040_STATE_MAGIC_2;
281             } else {
282                 flash040_context->flash_state = flash040_context->flash_base_state;
283             }
284             break;
285 
286         case FLASH040_STATE_MAGIC_2:
287             if (flash_magic_1(flash040_context, addr)) {
288                 switch (byte) {
289                     case 0x90:
290                         flash040_context->flash_state = FLASH040_STATE_AUTOSELECT;
291                         flash040_context->flash_base_state = FLASH040_STATE_AUTOSELECT;
292                         break;
293                     case 0xf0:
294                         flash040_context->flash_state = FLASH040_STATE_READ;
295                         flash040_context->flash_base_state = FLASH040_STATE_READ;
296                         break;
297                     case 0xa0:
298                         flash040_context->flash_state = FLASH040_STATE_BYTE_PROGRAM;
299                         break;
300                     case 0x80:
301                         flash040_context->flash_state = FLASH040_STATE_ERASE_MAGIC_1;
302                         break;
303                     default:
304                         flash040_context->flash_state = flash040_context->flash_base_state;
305                         break;
306                 }
307             } else {
308                 flash040_context->flash_state = flash040_context->flash_base_state;
309             }
310             break;
311 
312         case FLASH040_STATE_BYTE_PROGRAM:
313             if (flash_program_byte(flash040_context, addr, byte)) {
314                 /* The byte program time is short enough to ignore */
315                 flash040_context->flash_state = flash040_context->flash_base_state;
316             } else {
317                 flash040_context->flash_state = FLASH040_STATE_BYTE_PROGRAM_ERROR;
318             }
319             break;
320 
321         case FLASH040_STATE_ERASE_MAGIC_1:
322             if (flash_magic_1(flash040_context, addr) && (byte == 0xaa)) {
323                 flash040_context->flash_state = FLASH040_STATE_ERASE_MAGIC_2;
324             } else {
325                 flash040_context->flash_state = flash040_context->flash_base_state;
326             }
327             break;
328 
329         case FLASH040_STATE_ERASE_MAGIC_2:
330             if (flash_magic_2(flash040_context, addr) && (byte == 0x55)) {
331                 flash040_context->flash_state = FLASH040_STATE_ERASE_SELECT;
332             } else {
333                 flash040_context->flash_state = flash040_context->flash_base_state;
334             }
335             break;
336 
337         case FLASH040_STATE_ERASE_SELECT:
338             if (flash_magic_1(flash040_context, addr) && (byte == 0x10)) {
339                 flash040_context->flash_state = FLASH040_STATE_CHIP_ERASE;
340                 flash040_context->program_byte = 0;
341                 alarm_set(flash040_context->erase_alarm, maincpu_clk + flash_types[flash040_context->flash_type].erase_chip_cycles);
342             } else if (byte == 0x30) {
343                 flash_add_sector_to_erase_mask(flash040_context, addr);
344                 flash040_context->program_byte = 0;
345                 flash040_context->flash_state = FLASH040_STATE_SECTOR_ERASE_TIMEOUT;
346                 alarm_set(flash040_context->erase_alarm, maincpu_clk + flash_types[flash040_context->flash_type].erase_sector_timeout_cycles);
347             } else {
348                 flash040_context->flash_state = flash040_context->flash_base_state;
349             }
350             break;
351 
352         case FLASH040_STATE_SECTOR_ERASE_TIMEOUT:
353             if (byte == 0x30) {
354                 flash_add_sector_to_erase_mask(flash040_context, addr);
355             } else {
356                 flash040_context->flash_state = flash040_context->flash_base_state;
357                 flash_clear_erase_mask(flash040_context);
358                 alarm_unset(flash040_context->erase_alarm);
359             }
360             break;
361 
362         case FLASH040_STATE_SECTOR_ERASE:
363             /* TODO not all models support suspending */
364             if (byte == 0xb0) {
365                 flash040_context->flash_state = FLASH040_STATE_SECTOR_ERASE_SUSPEND;
366                 alarm_unset(flash040_context->erase_alarm);
367             }
368             break;
369 
370         case FLASH040_STATE_SECTOR_ERASE_SUSPEND:
371             if (byte == 0x30) {
372                 flash040_context->flash_state = FLASH040_STATE_SECTOR_ERASE;
373                 alarm_set(flash040_context->erase_alarm, maincpu_clk + flash_types[flash040_context->flash_type].erase_sector_cycles);
374             }
375             break;
376 
377         case FLASH040_STATE_BYTE_PROGRAM_ERROR:
378         case FLASH040_STATE_AUTOSELECT:
379             if (flash_magic_1(flash040_context, addr) && (byte == 0xaa)) {
380                 flash040_context->flash_state = FLASH040_STATE_MAGIC_1;
381             }
382             if (byte == 0xf0) {
383                 flash040_context->flash_state = FLASH040_STATE_READ;
384                 flash040_context->flash_base_state = FLASH040_STATE_READ;
385             }
386             break;
387 
388         case FLASH040_STATE_CHIP_ERASE:
389         default:
390             break;
391     }
392 
393     FLASH_DEBUG(("Write %02x to %05x, state %i->%i (base state %i->%i)", byte, addr, (int)old_state, (int)flash040_context->flash_state, (int)old_base_state, (int)flash040_context->flash_base_state));
394 }
395 
396 /* -------------------------------------------------------------------------- */
397 
flash040core_store(flash040_context_t * flash040_context,unsigned int addr,uint8_t byte)398 void flash040core_store(flash040_context_t *flash040_context, unsigned int addr, uint8_t byte)
399 {
400     if (maincpu_rmw_flag) {
401         maincpu_clk--;
402         flash040core_store_internal(flash040_context, addr, flash040_context->last_read);
403         maincpu_clk++;
404     }
405 
406     flash040core_store_internal(flash040_context, addr, byte);
407 }
408 
flash040core_read(flash040_context_t * flash040_context,unsigned int addr)409 uint8_t flash040core_read(flash040_context_t *flash040_context, unsigned int addr)
410 {
411     uint8_t value;
412 #ifdef FLASH_DEBUG_ENABLED
413     flash040_state_t old_state = flash040_context->flash_state;
414 #endif
415 
416     switch (flash040_context->flash_state) {
417         case FLASH040_STATE_AUTOSELECT:
418             if (flash040_context->flash_type == FLASH040_TYPE_032B_A0_1_SWAP) {
419                 if ((addr & 0xff) < 4) {
420                     addr = "\0\2\1\3"[addr & 0x3];
421                 }
422             }
423 
424             if ((addr & 0xff) == 0) {
425                 value = flash_types[flash040_context->flash_type].manufacturer_ID;
426             } else if ((addr & 0xff) == flash_types[flash040_context->flash_type].device_ID_addr) {
427                 value = flash_types[flash040_context->flash_type].device_ID;
428             } else if ((addr & 0xff) == 2) {
429                 value = 0;
430             } else {
431                 value = flash040_context->flash_data[addr];
432             }
433             break;
434 
435         case FLASH040_STATE_BYTE_PROGRAM_ERROR:
436             value = flash_write_operation_status(flash040_context);
437             break;
438 
439         case FLASH040_STATE_SECTOR_ERASE_SUSPEND:
440         case FLASH040_STATE_CHIP_ERASE:
441         case FLASH040_STATE_SECTOR_ERASE:
442         case FLASH040_STATE_SECTOR_ERASE_TIMEOUT:
443             value = flash_erase_operation_status(flash040_context);
444             break;
445 
446         default:
447         /* The state doesn't reset if a read occurs during a command sequence */
448         /* fall through */
449         case FLASH040_STATE_READ:
450             value = flash040_context->flash_data[addr];
451             break;
452     }
453 
454 #ifdef FLASH_DEBUG_ENABLED
455     if (old_state != FLASH040_STATE_READ) {
456         FLASH_DEBUG(("Read %02x from %05x, state %i->%i", value, addr, (int)old_state, (int)flash040_context->flash_state));
457     }
458 #endif
459 
460     flash040_context->last_read = value;
461     return value;
462 }
463 
flash040core_peek(flash040_context_t * flash040_context,unsigned int addr)464 uint8_t flash040core_peek(flash040_context_t *flash040_context, unsigned int addr)
465 {
466     return flash040_context->flash_data[addr];
467 }
468 
flash040core_reset(flash040_context_t * flash040_context)469 void flash040core_reset(flash040_context_t *flash040_context)
470 {
471     FLASH_DEBUG(("Reset"));
472     flash040_context->flash_state = FLASH040_STATE_READ;
473     flash040_context->flash_base_state = FLASH040_STATE_READ;
474     flash040_context->program_byte = 0;
475     flash_clear_erase_mask(flash040_context);
476     alarm_unset(flash040_context->erase_alarm);
477 }
478 
flash040core_init(struct flash040_context_s * flash040_context,struct alarm_context_s * alarm_context,flash040_type_t type,uint8_t * data)479 void flash040core_init(struct flash040_context_s *flash040_context,
480                        struct alarm_context_s *alarm_context,
481                        flash040_type_t type, uint8_t *data)
482 {
483     FLASH_DEBUG(("Init"));
484     flash040_context->flash_data = data;
485     flash040_context->flash_type = type;
486     flash040_context->flash_state = FLASH040_STATE_READ;
487     flash040_context->flash_base_state = FLASH040_STATE_READ;
488     flash040_context->program_byte = 0;
489     flash_clear_erase_mask(flash040_context);
490     flash040_context->flash_dirty = 0;
491     flash040_context->erase_alarm = alarm_new(alarm_context, "Flash040Alarm", erase_alarm_handler, flash040_context);
492 }
493 
flash040core_shutdown(flash040_context_t * flash040_context)494 void flash040core_shutdown(flash040_context_t *flash040_context)
495 {
496     FLASH_DEBUG(("Shutdown"));
497 }
498 
499 /* -------------------------------------------------------------------------- */
500 
501 #define FLASH040_DUMP_VER_MAJOR   2
502 #define FLASH040_DUMP_VER_MINOR   0
503 
flash040core_snapshot_write_module(snapshot_t * s,flash040_context_t * flash040_context,const char * name)504 int flash040core_snapshot_write_module(snapshot_t *s, flash040_context_t *flash040_context, const char *name)
505 {
506     snapshot_module_t *m;
507     uint8_t state, base_state;
508 
509     m = snapshot_module_create(s, name, FLASH040_DUMP_VER_MAJOR, FLASH040_DUMP_VER_MINOR);
510     if (m == NULL) {
511         return -1;
512     }
513 
514     state = (uint8_t)(flash040_context->flash_state);
515     base_state = (uint8_t)(flash040_context->flash_base_state);
516 
517     if (0
518         || (SMW_B(m, state) < 0)
519         || (SMW_B(m, base_state) < 0)
520         || (SMW_B(m, flash040_context->program_byte) < 0)
521         || (SMW_BA(m, flash040_context->erase_mask, FLASH040_ERASE_MASK_SIZE) < 0)
522         || (SMW_B(m, flash040_context->last_read) < 0)) {
523         snapshot_module_close(m);
524         return -1;
525     }
526 
527     snapshot_module_close(m);
528     return 0;
529 }
530 
flash040core_snapshot_read_module(snapshot_t * s,flash040_context_t * flash040_context,const char * name)531 int flash040core_snapshot_read_module(snapshot_t *s, flash040_context_t *flash040_context, const char *name)
532 {
533     uint8_t vmajor, vminor, state, base_state;
534     snapshot_module_t *m;
535 
536     m = snapshot_module_open(s, name, &vmajor, &vminor);
537     if (m == NULL) {
538         return -1;
539     }
540 
541     if (vmajor != FLASH040_DUMP_VER_MAJOR) {
542         snapshot_module_close(m);
543         return -1;
544     }
545 
546     if (0
547         || (SMR_B(m, &state) < 0)
548         || (SMR_B(m, &base_state) < 0)
549         || (SMR_B(m, &(flash040_context->program_byte)) < 0)
550         || (SMR_BA(m, flash040_context->erase_mask, FLASH040_ERASE_MASK_SIZE) < 0)
551         || (SMR_B(m, &(flash040_context->last_read)) < 0)) {
552         snapshot_module_close(m);
553         return -1;
554     }
555 
556     snapshot_module_close(m);
557 
558     flash040_context->flash_state = (flash040_state_t)state;
559     flash040_context->flash_base_state = (flash040_state_t)base_state;
560 
561     /* Restore alarm if needed */
562     switch (flash040_context->flash_state) {
563         case FLASH040_STATE_SECTOR_ERASE_TIMEOUT:
564         case FLASH040_STATE_SECTOR_ERASE:
565         case FLASH040_STATE_CHIP_ERASE:
566             /* the alarm timing is not saved, just use some value for now */
567             alarm_set(flash040_context->erase_alarm, maincpu_clk + flash_types[flash040_context->flash_type].erase_sector_cycles);
568             break;
569 
570         default:
571             break;
572     }
573 
574     return 0;
575 }
576