Mercurial > repos > blastem
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; |