Mercurial > repos > blastem
comparison cdd_mcu.c @ 2062:07ed42bd7b4c segacd
Some progress on CDC and CDD emulation. Now passes first 3 "CDC INIT" tests in mcd-verificator
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 28 Jan 2022 00:50:17 -0800 |
parents | 7c1760b5b3e5 |
children | 91e4d2fe5cd9 |
comparison
equal
deleted
inserted
replaced
2061:7c1760b5b3e5 | 2062:07ed42bd7b4c |
---|---|
4 #define SCD_MCLKS 50000000 | 4 #define SCD_MCLKS 50000000 |
5 #define CD_BLOCK_CLKS 16934400 | 5 #define CD_BLOCK_CLKS 16934400 |
6 #define CDD_MCU_DIVIDER 8 | 6 #define CDD_MCU_DIVIDER 8 |
7 #define SECTOR_CLOCKS (CD_BLOCK_CLKS/75) | 7 #define SECTOR_CLOCKS (CD_BLOCK_CLKS/75) |
8 #define NIBBLE_CLOCKS (CDD_MCU_DIVIDER * 77) | 8 #define NIBBLE_CLOCKS (CDD_MCU_DIVIDER * 77) |
9 #define BYTE_CLOCKS (SECTOR_CLOCKS/2352) // 96 | |
9 | 10 |
10 //lead in start max diameter 46 mm | 11 //lead in start max diameter 46 mm |
11 //program area start max diameter 50 mm | 12 //program area start max diameter 50 mm |
12 //difference 4 mm = 4000 um | 13 //difference 4 mm = 4000 um |
13 //radius difference 2 mm = 2000 um | 14 //radius difference 2 mm = 2000 um |
100 { | 101 { |
101 case DS_PLAY: | 102 case DS_PLAY: |
102 handle_seek(context); | 103 handle_seek(context); |
103 if (!context->seeking) { | 104 if (!context->seeking) { |
104 context->head_pba++; | 105 context->head_pba++; |
106 uint32_t lba = context->head_pba - LEADIN_SECTORS; | |
107 for (uint32_t i = 0; i < context->media->num_tracks; i++) | |
108 { | |
109 if (lba < context->media->tracks[i].fake_pregap) { | |
110 context->in_fake_pregap = 1; | |
111 break; | |
112 } | |
113 lba -= context->media->tracks[i].fake_pregap; | |
114 if (lba < context->media->tracks[i].start_lba) { | |
115 context->in_fake_pregap = 1; | |
116 break; | |
117 } | |
118 if (lba < context->media->tracks[i].end_lba) { | |
119 fseek(context->media->f, lba * 2352, SEEK_SET); | |
120 context->in_fake_pregap = 0; | |
121 break; | |
122 } | |
123 } | |
105 } | 124 } |
106 break; | 125 break; |
107 case DS_PAUSE: | 126 case DS_PAUSE: |
108 handle_seek(context); | 127 handle_seek(context); |
109 break; | 128 break; |
113 context->head_pba++; | 132 context->head_pba++; |
114 if (context->media && context->media->type == MEDIA_CDROM && context->media->num_tracks) { | 133 if (context->media && context->media->type == MEDIA_CDROM && context->media->num_tracks) { |
115 if (context->head_pba > 3*context->media->num_tracks + 1) { | 134 if (context->head_pba > 3*context->media->num_tracks + 1) { |
116 context->toc_valid = 1; | 135 context->toc_valid = 1; |
117 context->seeking = 1; | 136 context->seeking = 1; |
118 context->seek_pba = LEADIN_SECTORS + context->media->tracks[0].start_lba + context->media->tracks[0].fake_pregap; | 137 context->seek_pba = LEADIN_SECTORS + context->media->tracks[0].start_lba; |
119 context->status = DS_PAUSE; | 138 context->status = DS_PAUSE; |
120 } | 139 } |
121 | 140 |
122 } else { | 141 } else { |
123 context->status = DS_NO_DISC; | 142 context->status = DS_NO_DISC; |
168 context->status_buffer.format = SF_NOTREADY; | 187 context->status_buffer.format = SF_NOTREADY; |
169 } | 188 } |
170 break; | 189 break; |
171 case SF_TOCO: | 190 case SF_TOCO: |
172 if (context->toc_valid) { | 191 if (context->toc_valid) { |
173 lba_to_status(context, context->media->tracks[context->media->num_tracks - 1].end_lba + context->media->tracks[0].fake_pregap); | 192 lba_to_status(context, context->media->tracks[context->media->num_tracks - 1].end_lba + (context->media->num_tracks > 1 ? context->media->tracks[1].fake_pregap : 0)); |
174 context->status_buffer.format = SF_TOCO; | 193 context->status_buffer.format = SF_TOCO; |
175 } else { | 194 } else { |
176 context->status_buffer.format = SF_NOTREADY; | 195 context->status_buffer.format = SF_NOTREADY; |
177 } | 196 } |
178 break; | 197 break; |
188 context->status_buffer.format = SF_NOTREADY; | 207 context->status_buffer.format = SF_NOTREADY; |
189 } | 208 } |
190 break; | 209 break; |
191 case SF_TOCN: | 210 case SF_TOCN: |
192 if (context->toc_valid) { | 211 if (context->toc_valid) { |
193 lba_to_status(context, context->media->tracks[context->requested_track].start_lba + context->media->tracks[0].fake_pregap); | 212 uint32_t lba = context->media->tracks[context->requested_track - 1].start_lba; |
213 if (context->requested_track > 1) { | |
214 lba += context->media->tracks[1].fake_pregap; | |
215 } | |
216 lba_to_status(context, lba); | |
194 context->status_buffer.b.tocn.track_low = context->requested_track % 10; | 217 context->status_buffer.b.tocn.track_low = context->requested_track % 10; |
195 context->status_buffer.format = SF_TOCN; | 218 context->status_buffer.format = SF_TOCN; |
196 } else { | 219 } else { |
197 context->status_buffer.format = SF_NOTREADY; | 220 context->status_buffer.format = SF_NOTREADY; |
198 } | 221 } |
211 if (context->requested_format != SF_TOCN) { | 234 if (context->requested_format != SF_TOCN) { |
212 context->status_buffer.b.time.flags = 0; //TODO: populate these | 235 context->status_buffer.b.time.flags = 0; //TODO: populate these |
213 } | 236 } |
214 context->status_buffer.checksum = checksum((uint8_t *)&context->status_buffer); | 237 context->status_buffer.checksum = checksum((uint8_t *)&context->status_buffer); |
215 if (context->status_buffer.format != SF_NOTREADY) { | 238 if (context->status_buffer.format != SF_NOTREADY) { |
216 printf("CDD Status %d%d.%d%d%d%d%d%d.%d%d\n", | 239 printf("CDD Status %X%X.%X%X%X%X%X%X.%X%X\n", |
217 context->status_buffer.status, context->status_buffer.format, | 240 context->status_buffer.status, context->status_buffer.format, |
218 context->status_buffer.b.time.min_high, context->status_buffer.b.time.min_low, | 241 context->status_buffer.b.time.min_high, context->status_buffer.b.time.min_low, |
219 context->status_buffer.b.time.sec_high, context->status_buffer.b.time.sec_low, | 242 context->status_buffer.b.time.sec_high, context->status_buffer.b.time.sec_low, |
220 context->status_buffer.b.time.frame_high, context->status_buffer.b.time.frame_low, | 243 context->status_buffer.b.time.frame_high, context->status_buffer.b.time.frame_low, |
221 context->status_buffer.b.time.flags, context->status_buffer.checksum | 244 context->status_buffer.b.time.flags, context->status_buffer.checksum |
240 break; | 263 break; |
241 case CMD_STOP: | 264 case CMD_STOP: |
242 puts("CDD CMD: STOP"); | 265 puts("CDD CMD: STOP"); |
243 context->status = DS_STOP; | 266 context->status = DS_STOP; |
244 break; | 267 break; |
268 case CMD_READ: | |
269 case CMD_SEEK: { | |
270 if (context->status == DS_DOOR_OPEN || context->status == DS_TRAY_MOVING || context->status == DS_DISC_LEADOUT || context->status == DS_DISC_LEADIN) { | |
271 context->error_status = DS_CMD_ERROR; | |
272 break; | |
273 } | |
274 if (context->requested_format == SF_TOCT || context->requested_format == SF_TOCN) { | |
275 context->requested_format = SF_ABSOLUTE; | |
276 } | |
277 if (!context->toc_valid) { | |
278 context->error_status = DS_CMD_ERROR; | |
279 break; | |
280 } | |
281 uint32_t lba = context->cmd_buffer.b.time.min_high * 10 + context->cmd_buffer.b.time.min_low; | |
282 lba *= 60; | |
283 lba += context->cmd_buffer.b.time.sec_high * 10 + context->cmd_buffer.b.time.sec_low; | |
284 lba *= 75; | |
285 lba += context->cmd_buffer.b.time.frame_high * 10 + context->cmd_buffer.b.time.frame_low; | |
286 printf("READ/SEEK cmd for lba %d\n", lba); | |
287 if (lba >= context->media->tracks[0].fake_pregap + context->media->tracks[context->media->num_tracks - 1].end_lba) { | |
288 context->error_status = DS_CMD_ERROR; | |
289 break; | |
290 } | |
291 context->seek_pba = lba + LEADIN_SECTORS - 4; | |
292 context->seeking = 1; | |
293 context->status = context->cmd_buffer.cmd_type == CMD_READ ? DS_PLAY : DS_PAUSE; | |
294 break; | |
295 } | |
245 case CMD_REPORT_REQUEST: | 296 case CMD_REPORT_REQUEST: |
246 switch (context->cmd_buffer.b.format.status_type) | 297 switch (context->cmd_buffer.b.format.status_type) |
247 { | 298 { |
248 case SF_ABSOLUTE: | 299 case SF_ABSOLUTE: |
249 case SF_RELATIVE: | 300 case SF_RELATIVE: |
273 context->requested_format = SF_TOCT; | 324 context->requested_format = SF_TOCT; |
274 break; | 325 break; |
275 case SF_TOCN: | 326 case SF_TOCN: |
276 context->requested_track = context->cmd_buffer.b.format.track_high * 10; | 327 context->requested_track = context->cmd_buffer.b.format.track_high * 10; |
277 context->requested_track += context->cmd_buffer.b.format.track_low; | 328 context->requested_track += context->cmd_buffer.b.format.track_low; |
329 if (!context->media || context->requested_track > context->media->num_tracks) { | |
330 context->requested_format = SF_ABSOLUTE; | |
331 context->error_status = DS_CMD_ERROR; | |
332 } | |
278 context->status = DS_TOC_READ; | 333 context->status = DS_TOC_READ; |
279 context->seeking = 1; | 334 context->seeking = 1; |
280 context->seek_pba = 0; | 335 context->seek_pba = 0; |
281 context->requested_format = SF_TOCN; | 336 context->requested_format = SF_TOCN; |
282 break; | 337 break; |
290 | 345 |
291 #define BIT_HOCK 0x4 | 346 #define BIT_HOCK 0x4 |
292 #define BIT_DRS 0x2 | 347 #define BIT_DRS 0x2 |
293 #define BIT_DTS 0x1 | 348 #define BIT_DTS 0x1 |
294 | 349 |
295 void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array) | 350 void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array, lc8951* cdc) |
296 { | 351 { |
297 uint32_t cd_cycle = mclks_to_cd_block(cycle); | 352 uint32_t cd_cycle = mclks_to_cd_block(cycle); |
298 if (!(gate_array[GAO_CDD_CTRL] & BIT_HOCK)) { | 353 if (!(gate_array[GAO_CDD_CTRL] & BIT_HOCK)) { |
299 //it's a little unclear if this gates the actual cd block clock or just handshaking | 354 //it's a little unclear if this gates the actual cd block clock or just handshaking |
300 //assum it's actually the clock for now | 355 //assum it's actually the clock for now |
302 return; | 357 return; |
303 } | 358 } |
304 uint32_t next_subcode = context->last_subcode_cycle + SECTOR_CLOCKS; | 359 uint32_t next_subcode = context->last_subcode_cycle + SECTOR_CLOCKS; |
305 uint32_t next_nibble = context->current_status_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; | 360 uint32_t next_nibble = context->current_status_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; |
306 uint32_t next_cmd_nibble = context->current_cmd_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; | 361 uint32_t next_cmd_nibble = context->current_cmd_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; |
362 uint32_t next_byte = context->current_sector_byte >= 0 ? context->last_byte_cycle + BYTE_CLOCKS : CYCLE_NEVER; | |
307 for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER) | 363 for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER) |
308 { | 364 { |
309 if (context->cycle >= next_subcode) { | 365 if (context->cycle >= next_subcode) { |
310 context->last_subcode_cycle = context->cycle; | 366 context->last_subcode_cycle = context->cycle; |
311 next_subcode = context->cycle + SECTOR_CLOCKS; | 367 next_subcode = context->cycle + SECTOR_CLOCKS; |
312 update_status(context); | 368 update_status(context); |
313 next_nibble = context->cycle; | 369 next_nibble = context->cycle; |
314 context->current_status_nibble = 0; | 370 context->current_status_nibble = 0; |
315 gate_array[GAO_CDD_STATUS] |= BIT_DRS; | 371 gate_array[GAO_CDD_STATUS] |= BIT_DRS; |
372 if (context->status == DS_PLAY && !context->seeking) { | |
373 next_byte = context->cycle; | |
374 context->current_sector_byte = 0; | |
375 } | |
316 } | 376 } |
317 if (context->cycle >= next_nibble) { | 377 if (context->cycle >= next_nibble) { |
318 if (context->current_status_nibble == sizeof(cdd_status)) { | 378 if (context->current_status_nibble == sizeof(cdd_status)) { |
319 context->current_status_nibble = -1; | 379 context->current_status_nibble = -1; |
320 gate_array[GAO_CDD_STATUS] &= ~BIT_DRS; | 380 gate_array[GAO_CDD_STATUS] &= ~BIT_DRS; |
356 context->current_cmd_nibble++; | 416 context->current_cmd_nibble++; |
357 context->last_nibble_cycle = context->cycle; | 417 context->last_nibble_cycle = context->cycle; |
358 next_cmd_nibble = context->cycle + NIBBLE_CLOCKS; | 418 next_cmd_nibble = context->cycle + NIBBLE_CLOCKS; |
359 } | 419 } |
360 } | 420 } |
421 if (context->cycle >= next_byte) { | |
422 uint8_t byte; | |
423 if (context->in_fake_pregap) { | |
424 if (!context->current_sector_byte || (context->current_sector_byte >= 16)) { | |
425 byte = 0; | |
426 //TODO: error detection and correction bytes | |
427 } else if (context->current_sector_byte < 12) { | |
428 byte = 0xFF; | |
429 } else if (context->current_sector_byte == 12) { | |
430 uint32_t minute = ((context->head_pba - LEADIN_SECTORS) / 75) / 60; | |
431 byte = (minute % 10) | ((minute / 10 ) << 4); | |
432 } else if (context->current_sector_byte == 13) { | |
433 uint32_t seconds = ((context->head_pba - LEADIN_SECTORS) / 75) % 60; | |
434 byte = (seconds % 10) | ((seconds / 10 ) << 4); | |
435 } else if (context->current_sector_byte == 14) { | |
436 uint32_t frames = (context->head_pba - LEADIN_SECTORS) % 75; | |
437 byte = (frames % 10) | ((frames / 10 ) << 4); | |
438 } else { | |
439 byte = 1; | |
440 } | |
441 } else { | |
442 byte = fgetc(context->media->f); | |
443 } | |
444 lc8951_write_byte(cdc, cd_block_to_mclks(context->cycle), context->current_sector_byte++, byte); | |
445 context->last_byte_cycle = context->cycle; | |
446 next_byte = context->cycle + BYTE_CLOCKS; | |
447 if (context->current_sector_byte == 2352) { | |
448 context->current_sector_byte = -1; | |
449 | |
450 } | |
451 } | |
361 } | 452 } |
362 } | 453 } |
363 | 454 |
364 void cdd_mcu_start_cmd_recv(cdd_mcu *context, uint16_t *gate_array) | 455 void cdd_mcu_start_cmd_recv(cdd_mcu *context, uint16_t *gate_array) |
365 { | 456 { |
398 context->last_subcode_cycle -= cd_deduction; | 489 context->last_subcode_cycle -= cd_deduction; |
399 } | 490 } |
400 if (context->last_nibble_cycle != CYCLE_NEVER) { | 491 if (context->last_nibble_cycle != CYCLE_NEVER) { |
401 context->last_nibble_cycle -= cd_deduction; | 492 context->last_nibble_cycle -= cd_deduction; |
402 } | 493 } |
403 } | 494 if (context->last_byte_cycle != CYCLE_NEVER) { |
495 context->last_byte_cycle -= cd_deduction; | |
496 } | |
497 } |