comparison cdd_mcu.c @ 2152:c3ee42c89b27

More accurate seek times and basic emulation of extended time between CDD interrupts during coarse seeking
author Michael Pavone <pavone@retrodev.com>
date Wed, 06 Apr 2022 21:44:46 -0700
parents d9151d0894c7
children a8af8d898a7c
comparison
equal deleted inserted replaced
2151:01ad74197414 2152:c3ee42c89b27
83 #define SECTOR_LENGTH 17.3333f 83 #define SECTOR_LENGTH 17.3333f
84 // max diameter for program area 116 mm 84 // max diameter for program area 116 mm
85 // circumference ~ 364.42 mm 85 // circumference ~ 364.42 mm
86 // ~ 21 sectors per physical track at edge 86 // ~ 21 sectors per physical track at edge
87 // ~8 sectors per physical track at start of lead-in 87 // ~8 sectors per physical track at start of lead-in
88 #define COARSE_SEEK 2200 //made up numbers 88 #define COARSE_SEEK_TRACKS 57 //estimate based on seek test
89 #define FINE_SEEK 10
90 static void handle_seek(cdd_mcu *context) 89 static void handle_seek(cdd_mcu *context)
91 { 90 {
92 //TODO: more realistic seeking behavior 91 uint32_t old_coarse = context->coarse_seek;
93 if (context->seeking) { 92 if (context->seeking) {
94 if (context->seek_pba == context->head_pba) { 93 if (context->seek_pba == context->head_pba) {
95 context->seeking = 0; 94 context->seeking = 0;
95 context->coarse_seek = 0;
96 if (context->status == DS_PAUSE) { 96 if (context->status == DS_PAUSE) {
97 context->pause_pba = context->head_pba; 97 context->pause_pba = context->head_pba;
98 } 98 }
99 } else { 99 } else {
100 //TODO: better estimate of sectors per track at current head location 100 //TODO: better estimate of sectors per track at current head location
101 //TODO: drive will periodically lose tracking when seeking which slows
102 //things down from this ideal speed I'm curently estimating
101 float circumference = (MAX_CIRCUMFERENCE-MIN_CIRCUMFERENCE) * ((float)context->head_pba) / (74 * 60 * SECTORS_PER_SECOND) + MIN_CIRCUMFERENCE; 103 float circumference = (MAX_CIRCUMFERENCE-MIN_CIRCUMFERENCE) * ((float)context->head_pba) / (74 * 60 * SECTORS_PER_SECOND) + MIN_CIRCUMFERENCE;
102 float sectors_per_track = circumference / SECTOR_LENGTH; 104 float sectors_per_track = circumference / SECTOR_LENGTH;
103 uint32_t max_seek = sectors_per_track * 190; 105 uint32_t max_seek = sectors_per_track * COARSE_SEEK_TRACKS;
104 uint32_t min_seek = sectors_per_track; 106 uint32_t min_seek = sectors_per_track;
105 107
106 108 uint32_t old_pba = context->head_pba;
107 if (context->seek_pba > context->head_pba) { 109 if (context->seek_pba > context->head_pba) {
108 uint32_t seek_amount; 110 uint32_t seek_amount;
109 for (seek_amount = max_seek; seek_amount >= min_seek; seek_amount >>= 1) 111 for (seek_amount = max_seek; seek_amount >= min_seek; seek_amount >>= 1)
110 { 112 {
111 if (context->seek_pba - context->head_pba >= seek_amount) { 113 if (context->seek_pba - context->head_pba >= seek_amount) {
131 context->head_pba -= min_seek; 133 context->head_pba -= min_seek;
132 } else { 134 } else {
133 context->head_pba = 0; 135 context->head_pba = 0;
134 } 136 }
135 } 137 }
136 } 138 if (context->head_pba != old_pba + 1) {
139 context->coarse_seek++;
140 } else {
141 context->coarse_seek = 0;
142 }
143 }
144 }
145 if (old_coarse && !context->coarse_seek) {
137 } 146 }
138 } 147 }
139 148
140 static void lba_to_status(cdd_mcu *context, uint32_t lba) 149 static void lba_to_status(cdd_mcu *context, uint32_t lba)
141 { 150 {
171 break; 180 break;
172 case DS_PAUSE: 181 case DS_PAUSE:
173 handle_seek(context); 182 handle_seek(context);
174 if (!context->seeking) { 183 if (!context->seeking) {
175 context->head_pba++; 184 context->head_pba++;
176 if (context->head_pba > context->pause_pba + FINE_SEEK) { 185 //TODO: better estimate of sectors per track at current head location
177 context->head_pba = context->pause_pba - FINE_SEEK; 186 float circumference = (MAX_CIRCUMFERENCE-MIN_CIRCUMFERENCE) * ((float)context->head_pba) / (74 * 60 * SECTORS_PER_SECOND) + MIN_CIRCUMFERENCE;
187 float sectors_per_track = circumference / SECTOR_LENGTH;
188 //TODO: check the exact behavior during pause on hardware
189 uint32_t diff = sectors_per_track * 0.5f + 0.5f;
190 if (context->head_pba > context->pause_pba + diff) {
191 context->head_pba = context->pause_pba - diff;
178 if (context->head_pba < LEADIN_SECTORS) { 192 if (context->head_pba < LEADIN_SECTORS) {
179 context->head_pba = LEADIN_SECTORS; 193 context->head_pba = LEADIN_SECTORS;
180 } 194 }
181 } 195 }
182 } 196 }
214 } 228 }
215 } 229 }
216 break; 230 break;
217 } 231 }
218 uint8_t force_not_ready = 0; 232 uint8_t force_not_ready = 0;
219 if (context->seeking && context->head_pba - prev_pba != 1) { 233 if (context->coarse_seek && !(context->coarse_seek % 15)) {
234 //TODO: adjust seeking for focus error when these bad statuses happen
220 //BIOS depends on getting a not ready status during seeking to clear certain state 235 //BIOS depends on getting a not ready status during seeking to clear certain state
221 force_not_ready = context->status_buffer.format != SF_NOTREADY; 236 force_not_ready = context->status_buffer.format != SF_NOTREADY;
222 } 237 }
223 if (context->first_cmd_received) { 238 if (context->first_cmd_received) {
224 switch (force_not_ready ? SF_NOTREADY : context->requested_format) 239 switch (force_not_ready ? SF_NOTREADY : context->requested_format)
584 uint32_t next_nibble; 599 uint32_t next_nibble;
585 if (context->current_status_nibble > 0) { 600 if (context->current_status_nibble > 0) {
586 next_nibble = context->last_nibble_cycle + NIBBLE_CLOCKS; 601 next_nibble = context->last_nibble_cycle + NIBBLE_CLOCKS;
587 } else if (!context->current_status_nibble) { 602 } else if (!context->current_status_nibble) {
588 next_nibble = context->last_sector_cycle + PROCESSING_DELAY; 603 next_nibble = context->last_sector_cycle + PROCESSING_DELAY;
604 if (context->coarse_seek % 3) {
605 next_nibble += SECTOR_CLOCKS * (3 - (context->coarse_seek % 3));
606 }
589 } else { 607 } else {
590 next_nibble = CYCLE_NEVER; 608 next_nibble = CYCLE_NEVER;
591 } 609 }
592 uint32_t next_cmd_nibble = context->current_cmd_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; 610 uint32_t next_cmd_nibble = context->current_cmd_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER;
593 611
594 for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER) 612 for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER)
595 { 613 {
596 if (context->cycle >= next_subcode) { 614 if (context->cycle >= next_subcode) {
615 uint32_t old_coarse = context->coarse_seek;
597 context->last_sector_cycle = context->cycle; 616 context->last_sector_cycle = context->cycle;
598 next_subcode = context->cycle + SECTOR_CLOCKS; 617 next_subcode = context->cycle + SECTOR_CLOCKS;
599 update_status(context, gate_array); 618 update_status(context, gate_array);
600 next_nibble = context->cycle + PROCESSING_DELAY; 619 next_nibble = context->cycle + PROCESSING_DELAY;
620 if (context->coarse_seek % 3) {
621 next_nibble += SECTOR_CLOCKS * (3 - (context->coarse_seek % 3));
622 }
601 context->current_status_nibble = 0; 623 context->current_status_nibble = 0;
602 if (context->next_subcode_int_cycle != CYCLE_NEVER) { 624 if (context->next_subcode_int_cycle != CYCLE_NEVER) {
603 context->subcode_int_pending = 1; 625 context->subcode_int_pending = 1;
604 } 626 }
605 if ((context->status == DS_PLAY || context->status == DS_PAUSE) && context->head_pba >= LEADIN_SECTORS && !context->seeking) { 627 if ((context->status == DS_PLAY || context->status == DS_PAUSE) && context->head_pba >= LEADIN_SECTORS && !context->seeking) {
607 context->current_subcode_byte = 0; 629 context->current_subcode_byte = 0;
608 context->next_subcode_cycle = context->cycle; 630 context->next_subcode_cycle = context->cycle;
609 context->next_subcode_int_cycle = cd_block_to_mclks(next_subcode); 631 context->next_subcode_int_cycle = cd_block_to_mclks(next_subcode);
610 } else { 632 } else {
611 context->next_subcode_int_cycle = CYCLE_NEVER; 633 context->next_subcode_int_cycle = CYCLE_NEVER;
634 }
635 if (old_coarse != context->coarse_seek) {
636 context->next_int_cycle = cd_block_to_mclks(context->cycle + PROCESSING_DELAY + 7 * NIBBLE_CLOCKS);
637 if (context->coarse_seek % 3) {
638 context->next_int_cycle += cd_block_to_mclks(SECTOR_CLOCKS * (3 - (context->coarse_seek % 3)));
639 }
612 } 640 }
613 } 641 }
614 if (context->cycle >= next_nibble) { 642 if (context->cycle >= next_nibble) {
615 if (context->current_status_nibble == sizeof(cdd_status)) { 643 if (context->current_status_nibble == sizeof(cdd_status)) {
616 context->current_status_nibble = -1; 644 context->current_status_nibble = -1;
632 gate_array[ga_index] = value | (gate_array[ga_index] & 0xFF00); 660 gate_array[ga_index] = value | (gate_array[ga_index] & 0xFF00);
633 } else { 661 } else {
634 gate_array[ga_index] = (value << 8) | (gate_array[ga_index] & 0x00FF); 662 gate_array[ga_index] = (value << 8) | (gate_array[ga_index] & 0x00FF);
635 } 663 }
636 if (context->current_status_nibble == 7) { 664 if (context->current_status_nibble == 7) {
637 context->int_pending = 1; 665 if (!(context->coarse_seek % 3)) {
638 context->next_int_cycle = cd_block_to_mclks(context->cycle + SECTOR_CLOCKS); 666 context->int_pending = 1;
667 if (context->coarse_seek) {
668 context->next_int_cycle = cd_block_to_mclks(context->cycle + 3 * SECTOR_CLOCKS);
669 } else {
670 context->next_int_cycle = cd_block_to_mclks(context->cycle + SECTOR_CLOCKS);
671 }
672 }
639 } 673 }
640 context->current_status_nibble++; 674 context->current_status_nibble++;
641 context->last_nibble_cycle = context->cycle; 675 context->last_nibble_cycle = context->cycle;
642 next_nibble = context->cycle + NIBBLE_CLOCKS; 676 next_nibble = context->cycle + NIBBLE_CLOCKS;
643 } 677 }
724 758
725 void cdd_hock_enabled(cdd_mcu *context) 759 void cdd_hock_enabled(cdd_mcu *context)
726 { 760 {
727 context->last_sector_cycle = context->cycle; 761 context->last_sector_cycle = context->cycle;
728 context->next_int_cycle = cd_block_to_mclks(context->cycle + SECTOR_CLOCKS + PROCESSING_DELAY + 7 * NIBBLE_CLOCKS); 762 context->next_int_cycle = cd_block_to_mclks(context->cycle + SECTOR_CLOCKS + PROCESSING_DELAY + 7 * NIBBLE_CLOCKS);
763 if (context->coarse_seek % 3) {
764 context->next_int_cycle += cd_block_to_mclks(SECTOR_CLOCKS * (3 - (context->coarse_seek % 3)));
765 }
729 } 766 }
730 767
731 void cdd_hock_disabled(cdd_mcu *context) 768 void cdd_hock_disabled(cdd_mcu *context)
732 { 769 {
733 context->last_sector_cycle = CYCLE_NEVER; 770 context->last_sector_cycle = CYCLE_NEVER;