2 * BUBBLE CASETTE for FM-8/7? [bubblecasette.h]
4 * Author: K.Ohta <whatisthis.sowhat _at_ gmail.com>
7 * Mar 22, 2016 : Initial
11 #include "bubblecasette.h"
14 void BUBBLECASETTE::initialize()
17 header_changed = false;
19 write_protect = false;
30 void BUBBLECASETTE::reset()
37 if(header_changed) write_header();
38 header_changed = false;
45 write_access = read_access = false;
56 transfer_error = false;
57 bad_loop_over_error = false;
58 no_marker_error = false;
59 undefined_cmd_error = false;
62 uint32_t BUBBLECASETTE::read_io8(uint32_t address)
67 uint16_t page_size_tmp;
68 uint32_t media_size_tmp;
69 if(bubble_type == BUBBLE_TYPE_32KB) {
72 media_size_tmp = 0x8000;
73 } else if(bubble_type == BUBBLE_TYPE_64KB) {
76 media_size_tmp = 0x10000;
77 } else if(bubble_type == BUBBLE_TYPE_128KB) {
80 media_size_tmp = 0x20000;
82 //return val; // Not inserted.
85 switch(address & 15) {
86 case 0: // Data Resistor
87 val = (uint32_t)data_reg;
88 offset = (page_address.w.l & mask) * page_size_tmp + offset_reg;
90 if(offset >= media_size_tmp) {
100 if(offset_reg == page_size_tmp) {
105 //if(!read_one_page()) {
106 // stat_error = true;
108 // transfer_error = true; // Okay?
109 // // Error handling: End?
110 // page_count.w.l = 0;
112 if(page_count.w.l > 0) page_count.w.l--;
113 if((page_count.w.l > 0) && (offset < media_size_tmp)) {
117 page_address.w.l = (page_address.w.l + 1) & mask;
118 data_reg = bubble_data[page_address.w.l * page_size_tmp];
120 } else { // End multi read
123 //page_count.w.l = 0; // ??
127 data_reg = bubble_data[(page_address.w.l & mask) * page_size_tmp + offset_reg];
134 case 2: // Read status register
136 if(!bubble_inserted) {
142 val |= (cmd_error) ? 0x80 : 0;
143 val |= (stat_tdra) ? 0x40 : 0;
144 val |= (stat_rda) ? 0x20 : 0;
145 val |= (not_ready) ? 0x10 : 0;
146 val |= (write_protect) ? 0x04 : 0;
147 val |= (stat_error) ? 0x02 : 0;
148 val |= (stat_busy) ? 0x01 : 0;
149 if(!(bubble_inserted) && (stat_busy)) {
152 if((cmd_reg == 1) && (bubble_inserted) && (read_access)){
153 if(!stat_rda) stat_rda = true;
154 } else if((cmd_reg == 2) && (bubble_inserted) && (write_access)){
155 if(!stat_tdra) stat_tdra = true;
158 case 3: // Read Error register
160 val |= (eject_error) ? 0x80 : 0;
161 val |= (povr_error) ? 0x20 : 0;
162 val |= (crc_error) ? 0x10 : 0;
163 val |= (transfer_error) ? 0x08 : 0;
164 val |= (bad_loop_over_error) ? 0x04 : 0;
165 val |= (no_marker_error) ? 0x02 : 0;
166 val |= (undefined_cmd_error) ? 0x01 : 0;
169 val = page_address.b.h;
172 val = page_address.b.l;
175 val = page_count.b.h;
178 val = page_count.b.l;
182 if(bubble_inserted) {
183 val &= ~0x80; // page_address = 0000h
184 // val &= ~0x40; // page_address = 0400h
191 void BUBBLECASETTE::bubble_command(uint8_t cmd)
194 uint16_t page_size_tmp;
195 uint32_t media_size_tmp;
196 if(bubble_type == BUBBLE_TYPE_32KB) {
197 page_size_tmp = 0x20;
199 media_size_tmp = 0x8000;
200 } else if(bubble_type == BUBBLE_TYPE_64KB) {
201 page_size_tmp = 0x20;
203 media_size_tmp = 0x10000;
204 } else if(bubble_type == BUBBLE_TYPE_128KB) {
205 page_size_tmp = 0x40;
207 media_size_tmp = 0x20000;
213 write_access = false;
214 if(!bubble_inserted) {
217 no_marker_error = true;
220 data_reg = bubble_data[(page_address.w.l & mask) * page_size_tmp];
225 case 2: // Write :: Will not check insert?
227 write_access = false;
229 if(!bubble_inserted) {
232 no_marker_error = true;
256 write_access = read_access = false;
261 undefined_cmd_error = true;
265 void BUBBLECASETTE::write_io8(uint32_t address, uint32_t data)
270 uint16_t page_size_tmp;
271 uint32_t media_size_tmp;
272 if(bubble_type == BUBBLE_TYPE_32KB) {
273 page_size_tmp = 0x20;
275 media_size_tmp = 0x8000;
276 } else if(bubble_type == BUBBLE_TYPE_64KB) {
277 page_size_tmp = 0x20;
279 media_size_tmp = 0x10000;
280 } else if(bubble_type == BUBBLE_TYPE_128KB) {
281 page_size_tmp = 0x40;
283 media_size_tmp = 0x20000;
285 //return; // Not inserted.
289 switch(address & 15) {
290 case 0: // Write Data
293 if((offset = page_address.w.l * page_size_tmp + offset_reg) >= media_size_tmp) {
300 bubble_data[(page_address.w.l & mask) * page_size_tmp + offset_reg] = data_reg;
304 if(offset_reg == page_size_tmp) {
309 if(!write_one_page()) {
312 transfer_error = true; // Okay?
313 // Error handling: End?
318 if(page_count.w.l > 0) {
320 //page_address.w.l = (page_address.w.l + 1) & mask;
322 if((page_count.w.l > 0) && (offset < media_size_tmp)) {
325 page_address.w.l = (page_address.w.l + 1) & mask;
328 write_access = false;
332 //stat_tdra = false; // Move to event_callback()?
336 case 1: // CMD register
346 transfer_error = false;
347 bad_loop_over_error = false;
348 no_marker_error = false;
349 undefined_cmd_error = false;
358 page_address.b.h = val;
361 page_address.b.l = val;
364 page_count.b.h = val;
367 page_count.b.l = val;
372 uint32_t BUBBLECASETTE::read_signal(int address)
377 void BUBBLECASETTE::write_signal(int id, uint32_t data, uint32_t mask)
381 void BUBBLECASETTE::open(_TCHAR* file_path, int bank)
384 int contain_medias = 0;
386 header_changed = false;
391 media_offset_new = 0;
393 bubble_inserted = false;
398 stat_error = false; // OK?
401 memset(bubble_data, 0, 0x20000);
402 memset(&bbl_header, 0, sizeof(bbl_header_t));
409 if(fio == NULL) return;
411 memset(image_path, 0x00, _MAX_PATH * sizeof(_TCHAR));
412 _tcsncpy(image_path, file_path, _MAX_PATH);
414 if(fio->IsFileExisting(file_path)) {
415 fio->Fopen(file_path, FILEIO_READ_WRITE_BINARY);
416 file_length = fio->FileLength();
417 if(file_length == 0) return;
418 //printf("Size=%d\n", file_length);
419 if(file_length == 0x8000) { // 32KB
420 bubble_type = BUBBLE_TYPE_32KB;
422 } else if(file_length == 0x10000) {
423 bubble_type = BUBBLE_TYPE_64KB;
424 media_size = 0x10000;
425 } else if(file_length == 0x20000) {
426 bubble_type = BUBBLE_TYPE_128KB;
427 media_size = 0x20000;
429 bubble_type = BUBBLE_TYPE_B77;
432 if(bubble_type != BUBBLE_TYPE_B77) {
433 if(bubble_type < 0) return;
434 write_protect = false;
436 switch(bubble_type) {
437 case BUBBLE_TYPE_32KB:
438 fio->Fread(bubble_data, 0x8000, 1);
440 case BUBBLE_TYPE_64KB:
441 fio->Fread(bubble_data, 0x10000, 1);
443 case BUBBLE_TYPE_128KB:
444 fio->Fread(bubble_data, 0x20000, 1);
448 bubble_inserted = true;
452 write_protect = false;
454 if(!this->read_header()) break;
455 if(contain_medias != bank) {
456 fio->Fseek(media_offset_new , FILEIO_SEEK_SET); // Skip
457 if((uint32_t)fio->Ftell() >= file_length) return; // Error
458 } else { // Image found
459 if(bubble_type == BUBBLE_TYPE_32KB) {
460 if(bbl_header.offset.d > 0x20) fio->Fseek(media_offset, FILEIO_SEEK_SET);
461 remain = file_length - bbl_header.offset.d;
463 if(remain >= 0x8000) {
466 fio->Fread(bubble_data, remain, 1);
468 } else if(bubble_type == BUBBLE_TYPE_128KB) {
469 if(bbl_header.offset.d > 0x20) fio->Fseek(media_offset, FILEIO_SEEK_SET);
470 remain = file_length - bbl_header.offset.d;
471 media_size = 0x20000;
473 if(remain >= 0x20000) {
476 fio->Fread(bubble_data, remain, 1);
479 bubble_inserted = true;
480 media_num = (uint32_t)bank;
485 } while(contain_medias <= 16);
492 bool BUBBLECASETTE::read_header()
496 if(fio == NULL) return false;
497 if ((uint32_t)fio->Ftell() >= file_length) return false;
498 // You need convert to [UTF8|Local8Bit] when using UI.
499 fio->Fread(bbl_header.filename, 0x10, 1);
500 if(fio->Fread(&tmpval, 0x10, 1) != 0x10) return false;
502 // Offset(little endian)
503 bbl_header.offset.b.l = tmpval[4];
504 bbl_header.offset.b.h = tmpval[5];
505 bbl_header.offset.b.h2 = tmpval[6];
506 bbl_header.offset.b.h3 = tmpval[7];
507 // Casette size(little endian)
508 bbl_header.size.b.l = tmpval[12];
509 bbl_header.size.b.h = tmpval[13];
510 bbl_header.size.b.h2 = tmpval[14];
511 bbl_header.size.b.h3 = tmpval[15];
513 bbl_header.misc[0] = tmpval[0];
514 bbl_header.misc[1] = tmpval[1];
515 bbl_header.misc[2] = tmpval[2];
516 bbl_header.misc[3] = tmpval[3];
518 bbl_header.misc[4] = tmpval[8];
519 bbl_header.misc[5] = tmpval[9];
520 bbl_header.misc[6] = tmpval[10];
521 bbl_header.misc[7] = tmpval[11];
523 if((tmpval[10] & 0x10) != 0) {
524 write_protect = true; // ToDo : Relate to permission of image file.
526 write_protect = false; // ToDo : Relate to permission of image file.
530 bubble_type = BUBBLE_TYPE_32KB;
533 bubble_type = BUBBLE_TYPE_128KB;
539 media_size = bbl_header.size.d;
540 media_offset = media_offset_new + bbl_header.offset.d;
541 media_offset_new = media_offset_new + media_size;
545 void BUBBLECASETTE::write_header()
549 if(fio == NULL) return;
550 if((uint32_t)fio->Ftell() >= file_length) return;
551 // You need convert to [UTF8|Local8Bit] when using UI.
552 // Offset(little endian)
553 tmpval[4] = bbl_header.offset.b.l;
554 tmpval[5] = bbl_header.offset.b.h;
555 tmpval[6] = bbl_header.offset.b.h2;
556 tmpval[7] = bbl_header.offset.b.h3;
557 // Casette size(little endian)
558 tmpval[12] = bbl_header.size.b.l;
559 tmpval[13] = bbl_header.size.b.h;
560 tmpval[14] = bbl_header.size.b.h2;
561 tmpval[15] = bbl_header.size.b.h3;
563 tmpval[0] = bbl_header.misc[0];
564 tmpval[1] = bbl_header.misc[1];
565 tmpval[2] = bbl_header.misc[2];
566 tmpval[3] = bbl_header.misc[3];
568 tmpval[8] = bbl_header.misc[4];
569 tmpval[9] = bbl_header.misc[5];
570 tmpval[10] = bbl_header.misc[6];
571 tmpval[11] = bbl_header.misc[7];
576 tmpval[10] &= (uint8_t)(~0x10);
578 switch(bubble_type) {
579 case BUBBLE_TYPE_32KB:
582 case BUBBLE_TYPE_128KB:
589 fio->Fwrite(bbl_header.filename, 0x10, 1);
590 fio->Fwrite(&tmpval, 0x10, 1);
594 bool BUBBLECASETTE::read_one_page()
597 if(fio == NULL) return false;
598 if(!fio->IsOpened()) return false;
599 if(!bubble_inserted) {
603 f_pos = media_offset;
606 uint32_t page_size = 0;
607 int remain = (int)media_size - (int)bbl_header.offset.d;
608 if(remain <= 0) return false;
609 switch(bubble_type) {
610 case BUBBLE_TYPE_32KB:
611 offset = (page_address.w.l & 0x03ff) * 0x20;
614 case BUBBLE_TYPE_64KB:
615 offset = (page_address.w.l & 0x07ff) * 0x20;
618 case BUBBLE_TYPE_128KB:
619 offset = bbl_header.offset.d + (page_address.w.l & 0x07ff) * 0x40;
626 if(remain < (int)(offset + page_size)) return false;
627 fio->Fseek(f_pos + offset, FILEIO_SEEK_SET);
628 fio->Fread(&bubble_data[offset], page_size, 1);
633 bool BUBBLECASETTE::write_one_page()
636 if(fio == NULL) return false;
637 if(!fio->IsOpened()) return false;
638 if(!bubble_inserted) {
642 f_pos = media_offset;
645 uint32_t page_size = 0;
646 int remain = (int)media_size - (int)bbl_header.offset.d;
647 if(remain <= 0) return false;
648 switch(bubble_type) {
649 case BUBBLE_TYPE_32KB:
650 offset = (page_address.w.l & 0x03ff) * 0x20;
653 case BUBBLE_TYPE_64KB:
654 offset = (page_address.w.l & 0x07ff) * 0x20;
657 case BUBBLE_TYPE_128KB:
658 offset = bbl_header.offset.d + (page_address.w.l & 0x07ff) * 0x40;
665 //printf("Write One Page: PAGE=%04x COUNT=%04x:\n ",page_address.w.l, page_count.w.l);
666 if(remain < (int)(offset + page_size)) return false;
667 fio->Fseek(f_pos + offset, FILEIO_SEEK_SET);
668 fio->Fwrite(&bubble_data[offset], page_size, 1);
674 void BUBBLECASETTE::close()
678 if(fio->IsOpened()) {
679 if(is_wrote) write_one_page();
681 if(header_changed) write_header();
682 header_changed = false;
690 memset(image_path, 0x00, _MAX_PATH * sizeof(_TCHAR));
692 memset(&bbl_header, 0x00, sizeof(bbl_header_t));
693 memset(bubble_data, 0x00, 0x20000);
695 media_offset_new = 0;
698 header_changed = false;
699 bubble_inserted = false;
700 read_access = write_access = false;
703 void BUBBLECASETTE::event_callback(int event_id, int err)
707 #define STATE_VERSION 2
709 bool BUBBLECASETTE::process_state(FILEIO* state_fio, bool loading)
711 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
714 if(!state_fio->StateCheckInt32(this_device_id)) {
718 state_fio->StateUint32(file_length);
719 state_fio->StateBool(bubble_inserted);
720 state_fio->StateInt32(bubble_type);
721 state_fio->StateInt32(media_num);
722 state_fio->StateUint32(media_offset);
723 state_fio->StateUint32(media_offset_new);
724 state_fio->StateUint32(media_size);
726 state_fio->StateUint8(data_reg);
728 state_fio->StateUint8(cmd_reg);
730 state_fio->StateBool(stat_busy);
731 state_fio->StateBool(stat_tdra);
732 state_fio->StateBool(stat_rda);
733 state_fio->StateBool(cmd_error);
734 state_fio->StateBool(stat_error);
735 state_fio->StateBool(not_ready);
737 state_fio->StateBool(eject_error);
738 state_fio->StateBool(povr_error);
739 state_fio->StateBool(crc_error);
740 state_fio->StateBool(transfer_error);
741 state_fio->StateBool(bad_loop_over_error);
742 state_fio->StateBool(no_marker_error);
743 state_fio->StateBool(undefined_cmd_error);
745 state_fio->StateUint32(page_address.d);
747 state_fio->StateUint32(page_count.d);
749 state_fio->StateBool(is_b77);
750 state_fio->StateBool(read_access);
751 state_fio->StateBool(write_access);
752 state_fio->StateBool(write_protect);
753 state_fio->StateUint8(offset_reg);
755 state_fio->StateBuffer(image_path, _MAX_PATH * sizeof(_TCHAR), 1);
759 if(_tcslen(image_path) > 0) {
760 this->open(image_path, (int)media_num);
763 if(fio != NULL && fio->IsOpened()) {
764 if(is_wrote) write_one_page();
768 header_changed = false;