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 "../device.h"
12 #include "./bubblecasette.h"
16 BUBBLECASETTE::BUBBLECASETTE(VM_TEMPLATE* parent_vm, EMU *parent_emu) : DEVICE(parent_vm, parent_emu)
19 memset(image_path, 0x00, _MAX_PATH * sizeof(_TCHAR));
21 memset(&bbl_header, 0x00, sizeof(bbl_header_t));
22 memset(bubble_data, 0x00, 0x20000);
23 bubble_inserted = false;
24 read_access = write_access = false;
25 set_device_name(_T("FM Bubble Casette"));
29 BUBBLECASETTE::~BUBBLECASETTE()
33 void BUBBLECASETTE::initialize()
36 header_changed = false;
38 write_protect = false;
49 void BUBBLECASETTE::reset()
56 if(header_changed) write_header();
57 header_changed = false;
64 write_access = read_access = false;
75 transfer_error = false;
76 bad_loop_over_error = false;
77 no_marker_error = false;
78 undefined_cmd_error = false;
81 uint32_t BUBBLECASETTE::read_data8(uint32_t address)
86 uint16_t page_size_tmp;
87 uint32_t media_size_tmp;
88 if(bubble_type == BUBBLE_TYPE_32KB) {
91 media_size_tmp = 0x8000;
92 } else if(bubble_type == BUBBLE_TYPE_128KB) {
95 media_size_tmp = 0x20000;
97 //return val; // Not inserted.
100 switch(address & 7) {
101 case 0: // Data Resistor
102 val = (uint32_t)data_reg;
103 offset = (page_address.w.l & mask) * page_size_tmp + offset_reg;
105 if(offset >= media_size_tmp) {
112 write_access = false;
115 if(offset_reg == page_size_tmp) {
120 //if(!read_one_page()) {
121 // stat_error = true;
123 // transfer_error = true; // Okay?
124 // // Error handling: End?
125 // page_count.w.l = 0;
127 if(page_count.w.l > 0) page_count.w.l--;
128 if((page_count.w.l > 0) && (offset < media_size_tmp)) {
132 page_address.w.l = (page_address.w.l + 1) & mask;
133 data_reg = bubble_data[page_address.w.l * page_size_tmp];
135 } else { // End multi read
138 //page_count.w.l = 0; // ??
142 data_reg = bubble_data[(page_address.w.l & mask) * page_size_tmp + offset_reg];
149 case 2: // Read status register
151 if(!bubble_inserted) {
157 val |= (cmd_error) ? 0x80 : 0;
158 val |= (stat_tdra) ? 0x40 : 0;
159 val |= (stat_rda) ? 0x20 : 0;
160 val |= (not_ready) ? 0x10 : 0;
161 val |= (write_protect) ? 0x04 : 0;
162 val |= (stat_error) ? 0x02 : 0;
163 val |= (stat_busy) ? 0x01 : 0;
164 if(!(bubble_inserted) && (stat_busy)) {
167 if((cmd_reg == 1) && (bubble_inserted) && (read_access)){
168 if(!stat_rda) stat_rda = true;
169 } else if((cmd_reg == 2) && (bubble_inserted) && (write_access)){
170 if(!stat_tdra) stat_tdra = true;
173 case 3: // Read Error register
175 val |= (eject_error) ? 0x80 : 0;
176 val |= (povr_error) ? 0x20 : 0;
177 val |= (crc_error) ? 0x10 : 0;
178 val |= (transfer_error) ? 0x08 : 0;
179 val |= (bad_loop_over_error) ? 0x04 : 0;
180 val |= (no_marker_error) ? 0x02 : 0;
181 val |= (undefined_cmd_error) ? 0x01 : 0;
188 void BUBBLECASETTE::bubble_command(uint8_t cmd)
191 uint16_t page_size_tmp;
192 uint32_t media_size_tmp;
193 if(bubble_type == BUBBLE_TYPE_32KB) {
194 page_size_tmp = 0x20;
196 media_size_tmp = 0x8000;
197 } else if(bubble_type == BUBBLE_TYPE_128KB) {
198 page_size_tmp = 0x40;
200 media_size_tmp = 0x20000;
206 write_access = false;
207 if(!bubble_inserted) {
210 no_marker_error = true;
213 data_reg = bubble_data[(page_address.w.l & mask) * page_size_tmp];
218 case 2: // Write :: Will not check insert?
220 write_access = false;
222 if(!bubble_inserted) {
225 no_marker_error = true;
249 write_access = read_access = false;
254 undefined_cmd_error = true;
258 void BUBBLECASETTE::write_data8(uint32_t address, uint32_t data)
263 uint16_t page_size_tmp;
264 uint32_t media_size_tmp;
265 if(bubble_type == BUBBLE_TYPE_32KB) {
266 page_size_tmp = 0x20;
268 media_size_tmp = 0x8000;
269 } else if(bubble_type == BUBBLE_TYPE_128KB) {
270 page_size_tmp = 0x40;
272 media_size_tmp = 0x20000;
274 //return; // Not inserted.
278 switch(address & 7) {
279 case 0: // Write Data
282 if(offset >= media_size_tmp) {
289 offset = page_address.w.l * page_size_tmp + offset_reg;
290 bubble_data[(page_address.w.l & mask) * page_size_tmp + offset_reg] = data_reg;
294 if(offset_reg == page_size_tmp) {
299 if(!write_one_page()) {
302 transfer_error = true; // Okay?
303 // Error handling: End?
308 if(page_count.w.l > 0) {
310 //page_address.w.l = (page_address.w.l + 1) & mask;
312 if((page_count.w.l > 0) && (offset < media_size_tmp)) {
315 page_address.w.l = (page_address.w.l + 1) & mask;
318 write_access = false;
322 //stat_tdra = false; // Move to event_callback()?
326 case 1: // CMD register
336 transfer_error = false;
337 bad_loop_over_error = false;
338 no_marker_error = false;
339 undefined_cmd_error = false;
348 page_address.b.h = val;
351 page_address.b.l = val;
354 page_count.b.h = val;
357 page_count.b.l = val;
362 uint32_t BUBBLECASETTE::read_signal(int address)
366 void BUBBLECASETTE::write_signal(int id, uint32_t data, uint32_t mask)
370 bool BUBBLECASETTE::open(_TCHAR* file_path, int bank)
373 int contain_medias = 0;
375 header_changed = false;
380 media_offset_new = 0;
382 bubble_inserted = false;
387 stat_error = false; // OK?
390 memset(bubble_data, 0, 0x20000);
391 memset(&bbl_header, 0, sizeof(bbl_header_t));
398 if(fio == NULL) return false;
399 memset(image_path, 0x00, _MAX_PATH * sizeof(_TCHAR));
400 _tcsncpy(image_path, file_path, _MAX_PATH);
402 if(fio->IsFileExisting(file_path)) {
403 fio->Fopen(file_path, FILEIO_READ_WRITE_BINARY);
404 file_length = fio->FileLength();
405 if(file_length == 0) return false;
406 //printf("Size=%d\n", file_length);
407 if(file_length == 0x8000) { // 32KB
408 bubble_type = BUBBLE_TYPE_32KB;
410 } else if(file_length == 0x20000) {
411 bubble_type = BUBBLE_TYPE_128KB;
412 media_size = 0x20000;
414 bubble_type = BUBBLE_TYPE_B77;
417 if(bubble_type != BUBBLE_TYPE_B77) {
418 if(bubble_type < 0) return false;
419 write_protect = false;
421 switch(bubble_type) {
422 case BUBBLE_TYPE_32KB:
423 if(fio->Fread(bubble_data, 0x8000, 1) != 1) return false;
425 case BUBBLE_TYPE_128KB:
426 if(fio->Fread(bubble_data, 0x20000, 1) != 1) return false;
430 bubble_inserted = true;
435 write_protect = false;
437 if(!this->read_header()) break;
438 if(contain_medias != bank) {
439 fio->Fseek(media_offset_new , FILEIO_SEEK_SET); // Skip
440 if(fio->Ftell() >= file_length) return false; // Error
442 } else { // Image found
443 if(bubble_type == BUBBLE_TYPE_32KB) {
444 if(bbl_header.offset.d > 0x20) fio->Fseek(media_offset, FILEIO_SEEK_SET);
445 remain = file_length - bbl_header.offset.d;
447 if(remain >= 0x8000) {
450 fio->Fread(bubble_data, remain, 1);
452 } else if(bubble_type == BUBBLE_TYPE_128KB) {
453 if(bbl_header.offset.d > 0x20) fio->Fseek(media_offset, FILEIO_SEEK_SET);
454 remain = file_length - bbl_header.offset.d;
455 media_size = 0x20000;
457 if(remain >= 0x20000) {
460 fio->Fread(bubble_data, remain, 1);
463 bubble_inserted = true;
464 media_num = (uint32_t)bank;
468 } while(contain_medias <= 16);
469 if(contain_medias > 0) return true;
479 bool BUBBLECASETTE::read_header()
483 if(fio == NULL) return false;
484 if(fio->Ftell() >= file_length) return false;
485 // You need convert to [UTF8|Local8Bit] when using UI.
486 fio->Fread(bbl_header.filename, 0x10, 1);
487 if(fio->Fread(&tmpval, 0x10, 1) != 0x10) return false;
488 // Offset(little endian)
489 bbl_header.offset.b.l = tmpval[4];
490 bbl_header.offset.b.h = tmpval[5];
491 bbl_header.offset.b.h2 = tmpval[6];
492 bbl_header.offset.b.h3 = tmpval[7];
493 // Casette size(little endian)
494 bbl_header.size.b.l = tmpval[12];
495 bbl_header.size.b.h = tmpval[13];
496 bbl_header.size.b.h2 = tmpval[14];
497 bbl_header.size.b.h3 = tmpval[15];
499 bbl_header.misc[0] = tmpval[0];
500 bbl_header.misc[1] = tmpval[1];
501 bbl_header.misc[2] = tmpval[2];
502 bbl_header.misc[3] = tmpval[3];
504 bbl_header.misc[4] = tmpval[8];
505 bbl_header.misc[5] = tmpval[9];
506 bbl_header.misc[6] = tmpval[10];
507 bbl_header.misc[7] = tmpval[11];
509 if((tmpval[10] & 0x10) != 0) {
510 write_protect = true; // ToDo : Relate to permission of image file.
512 write_protect = false; // ToDo : Relate to permission of image file.
516 bubble_type = BUBBLE_TYPE_32KB;
519 bubble_type = BUBBLE_TYPE_128KB;
525 media_size = bbl_header.size.d;
526 media_offset = media_offset_new + bbl_header.offset.d;
527 media_offset_new = media_offset_new + media_size;
531 void BUBBLECASETTE::write_header()
535 if(fio == NULL) return;
536 if(fio->Ftell() >= file_length) return;
537 // You need convert to [UTF8|Local8Bit] when using UI.
538 // Offset(little endian)
539 tmpval[4] = bbl_header.offset.b.l;
540 tmpval[5] = bbl_header.offset.b.h;
541 tmpval[6] = bbl_header.offset.b.h2;
542 tmpval[7] = bbl_header.offset.b.h3;
543 // Casette size(little endian)
544 tmpval[12] = bbl_header.size.b.l;
545 tmpval[13] = bbl_header.size.b.h;
546 tmpval[14] = bbl_header.size.b.h2;
547 tmpval[15] = bbl_header.size.b.h3;
549 tmpval[0] = bbl_header.misc[0];
550 tmpval[1] = bbl_header.misc[1];
551 tmpval[2] = bbl_header.misc[2];
552 tmpval[3] = bbl_header.misc[3];
554 tmpval[8] = bbl_header.misc[4];
555 tmpval[9] = bbl_header.misc[5];
556 tmpval[10] = bbl_header.misc[6];
557 tmpval[11] = bbl_header.misc[7];
562 tmpval[10] &= (uint8_t)(~0x10);
564 switch(bubble_type) {
565 case BUBBLE_TYPE_32KB:
568 case BUBBLE_TYPE_128KB:
575 fio->Fwrite(bbl_header.filename, 0x10, 1);
576 fio->Fwrite(&tmpval, 0x10, 1);
580 bool BUBBLECASETTE::read_one_page()
583 if(fio == NULL) return false;
584 if(!fio->IsOpened()) return false;
585 if(!bubble_inserted) {
589 f_pos = media_offset;
592 uint32_t page_size = 0;
593 int remain = (int)media_size - (int)bbl_header.offset.d;
594 if(remain <= 0) return false;
595 switch(bubble_type) {
596 case BUBBLE_TYPE_32KB:
597 offset = (page_address.w.l & 0x03ff) * 0x20;
600 case BUBBLE_TYPE_128KB:
601 offset = bbl_header.offset.d + (page_address.w.l & 0x07ff) * 0x40;
608 if(remain < (int)(offset + page_size)) return false;
609 fio->Fseek(f_pos + offset, FILEIO_SEEK_SET);
610 fio->Fread(&bubble_data[offset], page_size, 1);
615 bool BUBBLECASETTE::write_one_page()
618 if(fio == NULL) return false;
619 if(!fio->IsOpened()) return false;
620 if(!bubble_inserted) {
624 f_pos = media_offset;
627 uint32_t page_size = 0;
628 int remain = (int)media_size - (int)bbl_header.offset.d;
629 if(remain <= 0) return false;
630 switch(bubble_type) {
631 case BUBBLE_TYPE_32KB:
632 offset = (page_address.w.l & 0x03ff) * 0x20;
635 case BUBBLE_TYPE_128KB:
636 offset = bbl_header.offset.d + (page_address.w.l & 0x07ff) * 0x40;
643 //printf("Write One Page: PAGE=%04x COUNT=%04x:\n ",page_address.w.l, page_count.w.l);
644 if(remain < (int)(offset + page_size)) return false;
645 fio->Fseek(f_pos + offset, FILEIO_SEEK_SET);
646 fio->Fwrite(&bubble_data[offset], page_size, 1);
652 void BUBBLECASETTE::close()
656 if(fio->IsOpened()) {
657 if(is_wrote) write_one_page();
659 if(header_changed) write_header();
660 header_changed = false;
668 memset(image_path, 0x00, _MAX_PATH * sizeof(_TCHAR));
670 memset(&bbl_header, 0x00, sizeof(bbl_header_t));
671 memset(bubble_data, 0x00, 0x20000);
673 media_offset_new = 0;
676 header_changed = false;
677 bubble_inserted = false;
678 read_access = write_access = false;
681 void BUBBLECASETTE::event_callback(int event_id, int err)
686 #define STATE_VERSION 5
688 bool BUBBLECASETTE::process_state(FILEIO *state_fio, bool loading)
690 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
693 if(!state_fio->StateCheckInt32(this_device_id)) {
697 state_fio->StateBool(is_wrote);
698 state_fio->StateBool(is_b77);
699 state_fio->StateBool(header_changed);
700 state_fio->StateBool(read_access);
701 state_fio->StateBool(write_access);
703 state_fio->StateUint8(offset_reg);
704 state_fio->StateUint8(data_reg);
705 state_fio->StateUint8(cmd_reg);
707 state_fio->StateBool(cmd_error);
708 state_fio->StateBool(stat_tdra);
709 state_fio->StateBool(stat_rda);
710 state_fio->StateBool(not_ready);
711 state_fio->StateBool(write_protect);
712 state_fio->StateBool(stat_error);
713 state_fio->StateBool(stat_busy);
715 state_fio->StateBool(eject_error);
716 state_fio->StateBool(povr_error);
717 state_fio->StateBool(crc_error);
718 state_fio->StateBool(transfer_error);
719 state_fio->StateBool(bad_loop_over_error);
720 state_fio->StateBool(no_marker_error);
721 state_fio->StateBool(undefined_cmd_error);
723 state_fio->StateUint32(page_address.d);
724 state_fio->StateUint32(page_count.d);
725 state_fio->StateBool(bubble_inserted);
726 state_fio->StateInt32(bubble_type);
727 state_fio->StateInt32(media_num);
729 state_fio->StateBuffer(image_path, sizeof(image_path), 1);
730 state_fio->StateBuffer((bbl_header.filename), sizeof(bbl_header.filename), 1);
731 state_fio->StateUint32((bbl_header.size.d));
732 state_fio->StateUint32((bbl_header.offset.d));
733 state_fio->StateBuffer((bbl_header.misc), sizeof(bbl_header.misc), 1);
736 state_fio->StateUint32(media_offset);
737 state_fio->StateUint32(media_offset_new);
738 state_fio->StateUint32(media_size);
739 state_fio->StateUint32(file_length);
740 state_fio->StateBuffer(bubble_data, sizeof(bubble_data), 1);
745 header_changed = false;
746 if(_tcslen(image_path) > 0) {
747 bool is_wrote_bak = is_wrote;
748 bool header_changed_bak = header_changed;
749 bool is_b77_bak = is_b77;
750 uint32_t media_offset_bak = media_offset;
751 uint32_t media_offset_new_bak = media_offset_new;
752 uint32_t file_length_bak = file_length;
753 bool bubble_inserted_bak = bubble_inserted;
754 bool not_ready_bak = not_ready;
755 bool cmd_error_bak = cmd_error;
756 bool stat_tdra_bak = stat_tdra;
757 bool stat_rda_bak = stat_rda;
758 bool stat_error_bak = stat_error; // OK?
759 bool stat_busy_bak = stat_busy;
760 int bubble_type_bak = bubble_type;
761 uint32_t media_size_bak = media_size;
762 bool write_protect_bak = write_protect;
763 bool not_ready_bak = not_ready;
764 uint32_t media_num_bak = media_num;
766 bbl_header_t bbl_header_bak;
767 uint8_t bubble_data_bak[0x20000];
768 _TCHAR image_path_bak[_MAX_PATH];
769 memcpy(&bbl_header_bak, &bbl_header, sizeof(bbl_header_t));
770 memcpy(bubble_data_bak, bubble_data, 0x20000);
771 memcpy(image_path_bak, image_path, _MAXPATH * sizeof(_TCHAR));
772 if(!open(image_path, (int)media_num)) {
773 // Revert loaded status
774 is_wrote = is_wrote_bak;
775 header_changed = header_changed_bak;
777 media_offset = media_offset_bak;
778 media_offset_new = media_offset_new_bak;
779 file_length = file_length_bak;
780 bubble_inserted = bubble_inserted_bak;
781 not_ready = not_ready_bak;
782 cmd_error = cmd_error_bak;
783 stat_tdra = stat_tdra_bak;
784 stat_rda = stat_rda_bak;
785 stat_error = stat_error_bak; // OK?
786 stat_busy = stat_busy_bak;
787 bubble_type = bubble_type_bak;
788 media_size = media_size_bak;
789 write_protect = write_protect_bak;
790 not_ready = not_ready_bak;
791 media_num = media_num_bak;
792 memcpy(&bbl_header, &bbl_header_bak, sizeof(bbl_header_t));
793 memcpy(bubble_data, bubble_data_bak, 0x20000);
794 memcpy(image_path, image_path_bak, _MAXPATH * sizeof(_TCHAR));
799 if(state_fio != NULL) {
800 if(state_fio->IsOpened()) {
801 if(is_wrote) write_one_page();
805 header_changed = false;