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"
14 BUBBLECASETTE::BUBBLECASETTE(VM_TEMPLATE* parent_vm, EMU *parent_emu) : DEVICE(parent_vm, parent_emu)
17 memset(image_path, 0x00, _MAX_PATH * sizeof(_TCHAR));
19 memset(&bbl_header, 0x00, sizeof(bbl_header_t));
20 memset(bubble_data, 0x00, 0x20000);
21 bubble_inserted = false;
22 read_access = write_access = false;
23 set_device_name(_T("FM Bubble Casette"));
27 BUBBLECASETTE::~BUBBLECASETTE()
31 void BUBBLECASETTE::initialize()
34 header_changed = false;
36 write_protect = false;
47 void BUBBLECASETTE::reset()
54 if(header_changed) write_header();
55 header_changed = false;
62 write_access = read_access = false;
73 transfer_error = false;
74 bad_loop_over_error = false;
75 no_marker_error = false;
76 undefined_cmd_error = false;
79 uint32_t BUBBLECASETTE::read_data8(uint32_t address)
84 uint16_t page_size_tmp;
85 uint32_t media_size_tmp;
86 if(bubble_type == BUBBLE_TYPE_32KB) {
89 media_size_tmp = 0x8000;
90 } else if(bubble_type == BUBBLE_TYPE_128KB) {
93 media_size_tmp = 0x20000;
95 //return val; // Not inserted.
99 case 0: // Data Resistor
100 val = (uint32_t)data_reg;
101 offset = (page_address.w.l & mask) * page_size_tmp + offset_reg;
103 if(offset >= media_size_tmp) {
110 write_access = false;
113 if(offset_reg == page_size_tmp) {
118 //if(!read_one_page()) {
119 // stat_error = true;
121 // transfer_error = true; // Okay?
122 // // Error handling: End?
123 // page_count.w.l = 0;
125 if(page_count.w.l > 0) page_count.w.l--;
126 if((page_count.w.l > 0) && (offset < media_size_tmp)) {
130 page_address.w.l = (page_address.w.l + 1) & mask;
131 data_reg = bubble_data[page_address.w.l * page_size_tmp];
133 } else { // End multi read
136 //page_count.w.l = 0; // ??
140 data_reg = bubble_data[(page_address.w.l & mask) * page_size_tmp + offset_reg];
147 case 2: // Read status register
149 if(!bubble_inserted) {
155 val |= (cmd_error) ? 0x80 : 0;
156 val |= (stat_tdra) ? 0x40 : 0;
157 val |= (stat_rda) ? 0x20 : 0;
158 val |= (not_ready) ? 0x10 : 0;
159 val |= (write_protect) ? 0x04 : 0;
160 val |= (stat_error) ? 0x02 : 0;
161 val |= (stat_busy) ? 0x01 : 0;
162 if(!(bubble_inserted) && (stat_busy)) {
165 if((cmd_reg == 1) && (bubble_inserted) && (read_access)){
166 if(!stat_rda) stat_rda = true;
167 } else if((cmd_reg == 2) && (bubble_inserted) && (write_access)){
168 if(!stat_tdra) stat_tdra = true;
171 case 3: // Read Error register
173 val |= (eject_error) ? 0x80 : 0;
174 val |= (povr_error) ? 0x20 : 0;
175 val |= (crc_error) ? 0x10 : 0;
176 val |= (transfer_error) ? 0x08 : 0;
177 val |= (bad_loop_over_error) ? 0x04 : 0;
178 val |= (no_marker_error) ? 0x02 : 0;
179 val |= (undefined_cmd_error) ? 0x01 : 0;
186 void BUBBLECASETTE::bubble_command(uint8_t cmd)
189 uint16_t page_size_tmp;
190 uint32_t media_size_tmp;
191 if(bubble_type == BUBBLE_TYPE_32KB) {
192 page_size_tmp = 0x20;
194 media_size_tmp = 0x8000;
195 } else if(bubble_type == BUBBLE_TYPE_128KB) {
196 page_size_tmp = 0x40;
198 media_size_tmp = 0x20000;
204 write_access = false;
205 if(!bubble_inserted) {
208 no_marker_error = true;
211 data_reg = bubble_data[(page_address.w.l & mask) * page_size_tmp];
216 case 2: // Write :: Will not check insert?
218 write_access = false;
220 if(!bubble_inserted) {
223 no_marker_error = true;
247 write_access = read_access = false;
252 undefined_cmd_error = true;
256 void BUBBLECASETTE::write_data8(uint32_t address, uint32_t data)
261 uint16_t page_size_tmp;
262 uint32_t media_size_tmp;
263 if(bubble_type == BUBBLE_TYPE_32KB) {
264 page_size_tmp = 0x20;
266 media_size_tmp = 0x8000;
267 } else if(bubble_type == BUBBLE_TYPE_128KB) {
268 page_size_tmp = 0x40;
270 media_size_tmp = 0x20000;
272 //return; // Not inserted.
276 switch(address & 7) {
277 case 0: // Write Data
280 if(offset >= media_size_tmp) {
287 offset = page_address.w.l * page_size_tmp + offset_reg;
288 bubble_data[(page_address.w.l & mask) * page_size_tmp + offset_reg] = data_reg;
292 if(offset_reg == page_size_tmp) {
297 if(!write_one_page()) {
300 transfer_error = true; // Okay?
301 // Error handling: End?
306 if(page_count.w.l > 0) {
308 //page_address.w.l = (page_address.w.l + 1) & mask;
310 if((page_count.w.l > 0) && (offset < media_size_tmp)) {
313 page_address.w.l = (page_address.w.l + 1) & mask;
316 write_access = false;
320 //stat_tdra = false; // Move to event_callback()?
324 case 1: // CMD register
334 transfer_error = false;
335 bad_loop_over_error = false;
336 no_marker_error = false;
337 undefined_cmd_error = false;
346 page_address.b.h = val;
349 page_address.b.l = val;
352 page_count.b.h = val;
355 page_count.b.l = val;
360 uint32_t BUBBLECASETTE::read_signal(int address)
364 void BUBBLECASETTE::write_signal(int id, uint32_t data, uint32_t mask)
368 bool BUBBLECASETTE::open(_TCHAR* file_path, int bank)
371 int contain_medias = 0;
373 header_changed = false;
378 media_offset_new = 0;
380 bubble_inserted = false;
385 stat_error = false; // OK?
388 memset(bubble_data, 0, 0x20000);
389 memset(&bbl_header, 0, sizeof(bbl_header_t));
396 if(fio == NULL) return false;
397 memset(image_path, 0x00, _MAX_PATH * sizeof(_TCHAR));
398 _tcsncpy(image_path, file_path, _MAX_PATH);
400 if(fio->IsFileExisting(file_path)) {
401 fio->Fopen(file_path, FILEIO_READ_WRITE_BINARY);
402 file_length = fio->FileLength();
403 if(file_length == 0) return false;
404 //printf("Size=%d\n", file_length);
405 if(file_length == 0x8000) { // 32KB
406 bubble_type = BUBBLE_TYPE_32KB;
408 } else if(file_length == 0x20000) {
409 bubble_type = BUBBLE_TYPE_128KB;
410 media_size = 0x20000;
412 bubble_type = BUBBLE_TYPE_B77;
415 if(bubble_type != BUBBLE_TYPE_B77) {
416 if(bubble_type < 0) return false;
417 write_protect = false;
419 switch(bubble_type) {
420 case BUBBLE_TYPE_32KB:
421 if(fio->Fread(bubble_data, 0x8000, 1) != 1) return false;
423 case BUBBLE_TYPE_128KB:
424 if(fio->Fread(bubble_data, 0x20000, 1) != 1) return false;
428 bubble_inserted = true;
433 write_protect = false;
435 if(!this->read_header()) break;
436 if(contain_medias != bank) {
437 fio->Fseek(media_offset_new , FILEIO_SEEK_SET); // Skip
438 if(fio->Ftell() >= file_length) return false; // Error
440 } else { // Image found
441 if(bubble_type == BUBBLE_TYPE_32KB) {
442 if(bbl_header.offset.d > 0x20) fio->Fseek(media_offset, FILEIO_SEEK_SET);
443 remain = file_length - bbl_header.offset.d;
445 if(remain >= 0x8000) {
448 fio->Fread(bubble_data, remain, 1);
450 } else if(bubble_type == BUBBLE_TYPE_128KB) {
451 if(bbl_header.offset.d > 0x20) fio->Fseek(media_offset, FILEIO_SEEK_SET);
452 remain = file_length - bbl_header.offset.d;
453 media_size = 0x20000;
455 if(remain >= 0x20000) {
458 fio->Fread(bubble_data, remain, 1);
461 bubble_inserted = true;
462 media_num = (uint32_t)bank;
466 } while(contain_medias <= 16);
467 if(contain_medias > 0) return true;
477 bool BUBBLECASETTE::read_header()
481 if(fio == NULL) return false;
482 if(fio->Ftell() >= file_length) return false;
483 // You need convert to [UTF8|Local8Bit] when using UI.
484 fio->Fread(bbl_header.filename, 0x10, 1);
485 if(fio->Fread(&tmpval, 0x10, 1) != 0x10) return false;
486 // Offset(little endian)
487 bbl_header.offset.b.l = tmpval[4];
488 bbl_header.offset.b.h = tmpval[5];
489 bbl_header.offset.b.h2 = tmpval[6];
490 bbl_header.offset.b.h3 = tmpval[7];
491 // Casette size(little endian)
492 bbl_header.size.b.l = tmpval[12];
493 bbl_header.size.b.h = tmpval[13];
494 bbl_header.size.b.h2 = tmpval[14];
495 bbl_header.size.b.h3 = tmpval[15];
497 bbl_header.misc[0] = tmpval[0];
498 bbl_header.misc[1] = tmpval[1];
499 bbl_header.misc[2] = tmpval[2];
500 bbl_header.misc[3] = tmpval[3];
502 bbl_header.misc[4] = tmpval[8];
503 bbl_header.misc[5] = tmpval[9];
504 bbl_header.misc[6] = tmpval[10];
505 bbl_header.misc[7] = tmpval[11];
507 if((tmpval[10] & 0x10) != 0) {
508 write_protect = true; // ToDo : Relate to permission of image file.
510 write_protect = false; // ToDo : Relate to permission of image file.
514 bubble_type = BUBBLE_TYPE_32KB;
517 bubble_type = BUBBLE_TYPE_128KB;
523 media_size = bbl_header.size.d;
524 media_offset = media_offset_new + bbl_header.offset.d;
525 media_offset_new = media_offset_new + media_size;
529 void BUBBLECASETTE::write_header()
533 if(fio == NULL) return;
534 if(fio->Ftell() >= file_length) return;
535 // You need convert to [UTF8|Local8Bit] when using UI.
536 // Offset(little endian)
537 tmpval[4] = bbl_header.offset.b.l;
538 tmpval[5] = bbl_header.offset.b.h;
539 tmpval[6] = bbl_header.offset.b.h2;
540 tmpval[7] = bbl_header.offset.b.h3;
541 // Casette size(little endian)
542 tmpval[12] = bbl_header.size.b.l;
543 tmpval[13] = bbl_header.size.b.h;
544 tmpval[14] = bbl_header.size.b.h2;
545 tmpval[15] = bbl_header.size.b.h3;
547 tmpval[0] = bbl_header.misc[0];
548 tmpval[1] = bbl_header.misc[1];
549 tmpval[2] = bbl_header.misc[2];
550 tmpval[3] = bbl_header.misc[3];
552 tmpval[8] = bbl_header.misc[4];
553 tmpval[9] = bbl_header.misc[5];
554 tmpval[10] = bbl_header.misc[6];
555 tmpval[11] = bbl_header.misc[7];
560 tmpval[10] &= (uint8_t)(~0x10);
562 switch(bubble_type) {
563 case BUBBLE_TYPE_32KB:
566 case BUBBLE_TYPE_128KB:
573 fio->Fwrite(bbl_header.filename, 0x10, 1);
574 fio->Fwrite(&tmpval, 0x10, 1);
578 bool BUBBLECASETTE::read_one_page()
581 if(fio == NULL) return false;
582 if(!fio->IsOpened()) return false;
583 if(!bubble_inserted) {
587 f_pos = media_offset;
590 uint32_t page_size = 0;
591 int remain = (int)media_size - (int)bbl_header.offset.d;
592 if(remain <= 0) return false;
593 switch(bubble_type) {
594 case BUBBLE_TYPE_32KB:
595 offset = (page_address.w.l & 0x03ff) * 0x20;
598 case BUBBLE_TYPE_128KB:
599 offset = bbl_header.offset.d + (page_address.w.l & 0x07ff) * 0x40;
606 if(remain < (int)(offset + page_size)) return false;
607 fio->Fseek(f_pos + offset, FILEIO_SEEK_SET);
608 fio->Fread(&bubble_data[offset], page_size, 1);
613 bool BUBBLECASETTE::write_one_page()
616 if(fio == NULL) return false;
617 if(!fio->IsOpened()) return false;
618 if(!bubble_inserted) {
622 f_pos = media_offset;
625 uint32_t page_size = 0;
626 int remain = (int)media_size - (int)bbl_header.offset.d;
627 if(remain <= 0) return false;
628 switch(bubble_type) {
629 case BUBBLE_TYPE_32KB:
630 offset = (page_address.w.l & 0x03ff) * 0x20;
633 case BUBBLE_TYPE_128KB:
634 offset = bbl_header.offset.d + (page_address.w.l & 0x07ff) * 0x40;
641 //printf("Write One Page: PAGE=%04x COUNT=%04x:\n ",page_address.w.l, page_count.w.l);
642 if(remain < (int)(offset + page_size)) return false;
643 fio->Fseek(f_pos + offset, FILEIO_SEEK_SET);
644 fio->Fwrite(&bubble_data[offset], page_size, 1);
650 void BUBBLECASETTE::close()
654 if(fio->IsOpened()) {
655 if(is_wrote) write_one_page();
657 if(header_changed) write_header();
658 header_changed = false;
666 memset(image_path, 0x00, _MAX_PATH * sizeof(_TCHAR));
668 memset(&bbl_header, 0x00, sizeof(bbl_header_t));
669 memset(bubble_data, 0x00, 0x20000);
671 media_offset_new = 0;
674 header_changed = false;
675 bubble_inserted = false;
676 read_access = write_access = false;
679 void BUBBLECASETTE::event_callback(int event_id, int err)
684 #define STATE_VERSION 5
686 bool BUBBLECASETTE::process_state(FILEIO *state_fio, bool loading)
688 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
691 if(!state_fio->StateCheckInt32(this_device_id)) {
695 state_fio->StateBool(is_wrote);
696 state_fio->StateBool(is_b77);
697 state_fio->StateBool(header_changed);
698 state_fio->StateBool(read_access);
699 state_fio->StateBool(write_access);
701 state_fio->StateUint8(offset_reg);
702 state_fio->StateUint8(data_reg);
703 state_fio->StateUint8(cmd_reg);
705 state_fio->StateBool(cmd_error);
706 state_fio->StateBool(stat_tdra);
707 state_fio->StateBool(stat_rda);
708 state_fio->StateBool(not_ready);
709 state_fio->StateBool(write_protect);
710 state_fio->StateBool(stat_error);
711 state_fio->StateBool(stat_busy);
713 state_fio->StateBool(eject_error);
714 state_fio->StateBool(povr_error);
715 state_fio->StateBool(crc_error);
716 state_fio->StateBool(transfer_error);
717 state_fio->StateBool(bad_loop_over_error);
718 state_fio->StateBool(no_marker_error);
719 state_fio->StateBool(undefined_cmd_error);
721 state_fio->StateUint32(page_address.d);
722 state_fio->StateUint32(page_count.d);
723 state_fio->StateBool(bubble_inserted);
724 state_fio->StateInt32(bubble_type);
725 state_fio->StateInt32(media_num);
727 state_fio->StateBuffer(image_path, sizeof(image_path), 1);
728 state_fio->StateBuffer((bbl_header.filename), sizeof(bbl_header.filename), 1);
729 state_fio->StateUint32((bbl_header.size.d));
730 state_fio->StateUint32((bbl_header.offset.d));
731 state_fio->StateBuffer((bbl_header.misc), sizeof(bbl_header.misc), 1);
734 state_fio->StateUint32(media_offset);
735 state_fio->StateUint32(media_offset_new);
736 state_fio->StateUint32(media_size);
737 state_fio->StateUint32(file_length);
738 state_fio->StateBuffer(bubble_data, sizeof(bubble_data), 1);
743 header_changed = false;
744 if(_tcslen(image_path) > 0) {
745 bool is_wrote_bak = is_wrote;
746 bool header_changed_bak = header_changed;
747 bool is_b77_bak = is_b77;
748 uint32_t media_offset_bak = media_offset;
749 uint32_t media_offset_new_bak = media_offset_new;
750 uint32_t file_length_bak = file_length;
751 bool bubble_inserted_bak = bubble_inserted;
752 bool not_ready_bak = not_ready;
753 bool cmd_error_bak = cmd_error;
754 bool stat_tdra_bak = stat_tdra;
755 bool stat_rda_bak = stat_rda;
756 bool stat_error_bak = stat_error; // OK?
757 bool stat_busy_bak = stat_busy;
758 int bubble_type_bak = bubble_type;
759 uint32_t media_size_bak = media_size;
760 bool write_protect_bak = write_protect;
761 bool not_ready_bak = not_ready;
762 uint32_t media_num_bak = media_num;
764 bbl_header_t bbl_header_bak;
765 uint8_t bubble_data_bak[0x20000];
766 _TCHAR image_path_bak[_MAX_PATH];
767 memcpy(&bbl_header_bak, &bbl_header, sizeof(bbl_header_t));
768 memcpy(bubble_data_bak, bubble_data, 0x20000);
769 memcpy(image_path_bak, image_path, _MAXPATH * sizeof(_TCHAR));
770 if(!open(image_path, (int)media_num)) {
771 // Revert loaded status
772 is_wrote = is_wrote_bak;
773 header_changed = header_changed_bak;
775 media_offset = media_offset_bak;
776 media_offset_new = media_offset_new_bak;
777 file_length = file_length_bak;
778 bubble_inserted = bubble_inserted_bak;
779 not_ready = not_ready_bak;
780 cmd_error = cmd_error_bak;
781 stat_tdra = stat_tdra_bak;
782 stat_rda = stat_rda_bak;
783 stat_error = stat_error_bak; // OK?
784 stat_busy = stat_busy_bak;
785 bubble_type = bubble_type_bak;
786 media_size = media_size_bak;
787 write_protect = write_protect_bak;
788 not_ready = not_ready_bak;
789 media_num = media_num_bak;
790 memcpy(&bbl_header, &bbl_header_bak, sizeof(bbl_header_t));
791 memcpy(bubble_data, bubble_data_bak, 0x20000);
792 memcpy(image_path, image_path_bak, _MAXPATH * sizeof(_TCHAR));
797 if(state_fio != NULL) {
798 if(state_fio->IsOpened()) {
799 if(is_wrote) write_one_page();
803 header_changed = false;